{"binary":false,"name":"README","content":"\n  bar -- BAshRulez the bash rule evaluator\n\nABOUT\n\n  Bar is a command runner that determines the order of commands and which ones to run, based\n  on rules. It lets the user define rules that dependently and conditionally evaluate shell\n  statements.  This is similar to other tools such as 'make' or 'just' but adds some\n  opinionated differences. There is support for using bar as driver for githooks for\n  automating workflows.\n\n  The notable differences to other similar tools are:\n\n  * First and foremost, bar is *not* a build tool. It is a engine that makes shell scripting\n    declarative.  Writing rules is much more descriptive than branching in a lot if/else\n    statements and it optimizes better as well.\n  * Batteries included! bar comes with ready to use modules, they vendor themself on first use.\n  * Rule targets and dependencies are commands; not artifacts like files/timestamps.\n  * Rules in bar can have multiple clauses.\n  * By default rules are conjunctive, when a clause of a rule fails then the rule fails and\n    not further evaluated.\n  * Rules can be made disjunctive, then a rule succeeds on the first succeeding clause.\n  * Clauses can be conditionally skipped.\n  * The results of rules are cached. They are evaluated only once, although there is\n    a form to define rules that always evaluate.\n  * We have a module that can memorize commands persistently and schedule evaluation to the\n    background. A later instance can pick up the results from this background evaluation.\n  * Deliberately implemented in bash, bar is meant to be vendored into any project using it,\n    it has very modest dependencies and should run anywhere you have a unix like userland with\n    bash and a few common tools available.\n  * Hackable, being veondored into your project means you can modify and edit everything to\n    your needs.\n\n  When you read this because you have seen 'bar' or '.bar' used in a repository then you may\n  look at INITIAL INSTALLATION below.\n\n\nQUICKSTART\n\n  When you cloned a project that uses bar then you can:\n\n   ./bar                # just runs whatever the maintainer decided as default\n   ./bar watch          # watches the project for changes and runs the default\n   ./bar watch fastchk  # watches the project for changes and runs fast checks\n   ./bar activate       # installs githooks and other local prep work\n\n  When you want to install and use it in your own projects:\n\n   git clone https://seed.pipapo.org/z3WhBdHt1VVNyeQ61zpY66pNzuSrP.git bar\n   cd bar\n   ./bar init_install ~/.local/\n   git pull   # to get updates\n\n  Then you can initialize it in your projects with:\n\n   bar init\n   git add Barf Bar.d .gitignore\n   git commit -m 'start using bar'\n\n   bar init_update  # for updating the version stored in the project\n\n  The second personality of bar is 'makeitso' eg.:\n\n   makeitso wake <hostname>     # sends a wake-on-lan packet to a host\n   makeitso suspend <hostname>  # remotely sends a host to suspend-to-ram\n\n  Buildin help system:\n\n   bar help [topic]\n   ./bar help [topic]\n\n  A rule file may be written in a syntax close to makefiles and justfiles, see RULE FILE\n  PREPROCESSOR below.\n\n\nINVOCATION AND SEMANTICS\n\n   bar [rulefile] [--bare] [rule [arguments..]]\n   makeitso [rulefile] [--bare] [rule [arguments..]]\n   ./bar [rulefile] [--bare] [rule [arguments..]]\n   <symlink> [arguments..]\n\n  Starts by loading all initial modules (those which have underscores in their name) and the\n  rulefile, which is either \"Barf\", \"barf\", \".Barf\", \".barf\" when called as 'bar' or \"Makeitsof\"\n  \"makeitsof\" \".Makeitsof\" \".makeitsof\" \"$HOME/.Makeitsof\" when called as 'makeitso' and when the first\n  argument is not a file.\n\n  The duality of having 'bar' installed as 'makeitso' is not only for making it more\n  polite. This allows for a user to have private '.Makeitsof' defining rules that are for common\n  administration tasks distinct from versioned per-project 'Barf' and 'Bar.d' rules.\n\n  Note that the rules for looking up the Barf/Makeitsof and the modules are somewhat distinct.\n  Nevertheless modules are in either way only loaded from a single directory and are not\n  located in different paths. This is a opinionated choice to make the module system\n  self-contained and not depend on secondary locations.\n\n  That single directory is the closest rule directory ('Bar.d', 'bar.d', '.Bar.d' or '.bar.d')\n  in the current directory or, walking upwards, in an ancestor of it.  The walk stops at the\n  filesystem boundary unless '$BAR_CROSS_FS' is set, and at a git repository which carries no\n  rule directory unless '$BAR_CROSS_REPO' is set: such a repository is a project of its own, and\n  its rulefile, state and logs belong below it, never to an ancestor.  When no rule directory\n  exists, the modules installed below '$BAR_PREFIX' are used; at the root of a git repository the\n  modules beside the running 'bar' serve instead, so a source tree can initialize a project\n  without being installed ('<checkout>/bar' with '<checkout>/Bar.d').  '$BAR_TOPLEVEL' stays the\n  directory bar was invoked from: it is the directory that holds the rule directory, and the\n  rulefile lookup and everything else bar keeps in the project (the memodb state, run logs)\n  resolves below it, never below an ancestor or the filesystem root.\n\n  Then the given rule (or MAIN) with the supplied arguments becomes evaluated. Finally, if\n  a 'CLEANUP' rule exists, it is evaluated.\n\n  The exit code of the invocation is the exit code of the main rule.\n\n  Just calling 'bar' without any arguments will evaluate the 'MAIN' rule which should be what\n  people expect by default. Other useful rules like 'help' for this documentation are defined\n  by modules.\n\n  When called by a symlink, the standard rulefile is loaded and instead of ‘MAIN’, a rule with\n  the same name as the symlink is called with all arguments passed.\n\n\nINITIAL INSTALLATION\n\n  Bar can be invoked in different ways:\n\n  1.  Installed in $PATH:\n      To make this work it is best to clone bar locally and symlink the checked out files\n      to your '.local/' tree. This allows easy upgrades via git:\n\n      When you have radicle (https://radicle.xyz/) installed you can clone it with:\n\n       rad clone rad:z3WhBdHt1VVNyeQ61zpY66pNzuSrP\n\n      Otherwise it can be cloned by git with:\n\n       git clone https://seed.pipapo.org/z3WhBdHt1VVNyeQ61zpY66pNzuSrP.git bar\n\n      Then you can install it in your ~/.local tree with:\n\n       cd bar\n       ./bar init_install ~/.local/\n\n      This creates symlinks to 'bar', 'makeitso', 'Bar.d', 'Barf.default', 'Makeitso.default' in ~/.local\n      and symlinks 'Bar.d/*' to '~/.config/makeitso/'.  Existing symlinks are left untouched and only\n      the ones for modules which are not linked yet are added, so a module which was added to\n      'Bar.d/' later becomes reachable by running 'init_install' again (no manual symlink needed).\n\n      Now 'bar' is installed, check it with 'bar help'\n\n  2.  The local './bar' initialized from above:\n      Since bar is a bash script, it’s easy to version and ship it with other software.\n      To initialize it in a current directory use either of:\n\n       bar init          # creates bar, Barf, Bar.d/\n       bar init --hidden # creates .bar, .Barf, .Bar.d/\n\n      This installs the modules bar needs at startup and then completes 'Bar.d/' from the installed\n      'Barf' ('bar init_deps').  With 'bar init --no-install-deps' that last step is left out, so\n      the 'Barf' can be customized first: 'bar init_deps' afterwards vendors the modules the\n      edited 'Barf' names.\n\n      After that Barf can be customized, unnecessary modules in Bar.d/ may be deleted ('bar\n      modules_prune' deletes the ones which are still unmodified copies of the installed ones and\n      completes the tree again).  Once that is done these files should be put under version\n      control.\n\n      When the installed bar from 1. becomes updated then a local version can be updated by:\n\n       bar init --update\n\n      This only updates 'bar' and any existing modules in 'Bar.d/' but will not touch the\n      'Barf' file since that is meant for local customization.\n\n       bar init_update_merge_barf\n\n      Will do a merge of the installed 'Barf' file with the local one. This *will* leave 3-way\n      conflict markers behind when there are changes. These must be manually resolved!\n\n      The updated files should then be committed to version control.\n\n  3.  githooks symlinked from './.git/hooks/*' -> '../../bar'\n      'bar' has support to be used as githooks. This needs manual enabling. Individual hooks can be\n      enabled or disabled with 'bar githook_enable <hook>' and 'bar githook_disable <hook>'.\n      For existing projects using 'bar' a maintainer can setup rules to activate all necessary hooks.\n      This then can be activated by './bar activate'.\n\n\nGIT HOOK ACTIVATION\n\n  Bar will be inert in a project that ships with bar initialized. One can call it manually by\n  './bar'.  A maintainer of the project may define 'activate' rules that activate\n  githooks. These are then activated by './bar activate'. This will create symlinks in the\n  '.git/hooks/' directory to the 'bar' script. These hooks will then be executed when the\n  respective git hook is triggered. The rules for these hooks are defined in the 'Barf' file\n  in the project directory.\n\n\nRULE SEMANTICS\n\n  Rules are a named, ordered collection of clauses. They are considered pure with inputs being\n  the current directory name and the rule's arguments. Note that the content of files is *not*\n  considered and the outcome of the rule should not depend on external variables. Rules are\n  evaluated lazily, only once; the result of a rule is cached. This means that rules, esp. the\n  actions within the body must have deterministic sematics. For performance reasons this is\n  not enforced but one may observe surprising behavior when this requirement is violated.\n\n  Rules will be autoloaded on demand from modules in 'Bar.d/' (see below AUTOMATIC MODULE\n  LOADING). For rules where this fails a fallback exits that creates an implicit rule for any\n  command and shell function defined with a body calling the respective command.\n\n  Each clause in a rule has its own set of dependencies. Dependencies are other rules which\n  are evaluated in order. When a (normal) dependency fails then rule it is considered to be\n  failed as well and no further attempts to evaluate following dependencies and clauses are\n  made.\n\n  Dependencies can be used as 'checking' dependency which either expects the dependency to\n  succeed (suffixed with a '?') or fail (prefixed with a '!'). When such a checking dependency\n  fails then the rest of the current clause is skipped. This allows conditional evaluation.\n  Further dependencies may be tagged as unconditional by suffix them when a tilde '~', these\n  are just evaluated but rules evaluation will proceed unconditionally even if such a\n  dependency fails.\n\n  Finally every clause can have an optional body. This are shell commands executed when all\n  dependencies succeeded. Because of quotiung in shell becomes a but unwieldly there are\n  shortcuts to make the body refer to shell functions, this should be preferred.\n\n\nRULE SYNTAX\n\n  A rule file, the 'Barf' and every module in 'Bar.d/', may be written in a syntax close to\n  the one of makefiles and justfiles.  'bar' runs a preprocessor over it which expands that syntax\n  into the 'rule' statements of RULE DEFINITION SYNTAX below and passes every other line through\n  unchanged: it never refuses a line, what it does not know is reported by the parser, at the line\n  it is on.  Both syntaxes may be mixed in one file, and for a rule file you write this is the\n  preferred one: 'rule' is the plain form it expands to, for what it cannot express.\n  'bar preprocess <file>' prints the text which gets sourced.\n\n   <name>: <deps>..                    rule <name>: <deps>..\n   [<rule-flag>]..                     attached to the next header\n   <name>: [<clause-flag>].. <deps>..  rule <name>: --<clause-flag>.. <deps>..\n   <name>: <deps>.. ; <body>           rule <name>: <deps>.. -- <body>\n   <name>:                             an indented block below is the body\n   <name>: (<dep> <arg>..)             one dependency, quoted, arguments included\n   <name>: <deps>.. <var>[=<value>]..  rule <name>: <deps>.. 'clause_local <var>[=<value>]..' ..\n   <NAME> := <value>                   <NAME>=<value>\n   <NAME> ?= <value>                   : \"${<NAME>:=<value>}\"\n\n  * A header is a line which starts at column 0 with a name and a colon. Indented lines,\n    heredoc bodies and multi line quoted strings are never touched.\n\n  * '[<rule-flag>]..' are stacked lines at column 0, each a '--rule-flag' of RULE DEFINITION\n    SYNTAX without the dashes. They attach to the next header. They are printed as comments\n    ('# [always]'): the flags travel on the rule line, and one line in stays one line out, so a\n    parse error names the line of the file.\n\n  * '[<clause-flag>]..' after the colon are the '--clause-flag' of RULE DEFINITION SYNTAX\n    without the dashes, brackets stack.\n\n  * '; <body>' ends the dependencies, everything behind it is the clause body.\n\n  * The body of an indented block is the block's text: the indentation of its first line is\n    stripped from all its lines, blank lines belong to it, and it ends at the first non blank\n    line indented less.\n\n  * A run of variable definitions among the dependencies is one dependency 'clause_local' which\n    carries the run: '<var>=<value>' makes the variable local to the clause with that value,\n    '<var>=' with the empty one, and '=<var>' with the value the outer scope has. Two runs are\n    never coalesced, the order is kept, and a run ends at the first word which is no definition.\n\n  * '(<dep> <arg>..)' is one dependency which carries arguments. Modifiers written outside the\n    parentheses are lifted onto it, '!(dep arg)' is the dependency '!dep arg'. Parentheses may\n    nest and inner quoting is kept as written.\n\n  * 'NAME := value' assigns a variable, 'NAME ?= value' assigns it only when it is unset or\n    empty.\n\n  * Shellcheck checks a module and a rule file through this preprocessor ('bar_lint'), so the\n    make/just syntax is not reported as a syntax error and only genuine shell defects are.\n\n  * One filter serves a whole 'bar' invocation and is handed one file at a time: a file expands to\n    what it expands to alone, never depending on the files before it.\n\n\n  Examples:\n\n   # Different ways to define rule bodies\n   foo_ok: ; echo inline\n   foo_ok:\n      echo indented\n\n   foo_ok: -- <<<\"echo herestring\"\n   foo_ok: -- <<EOR\n      echo heredoc\n   EOR\n\n   # Make a rule that fails\n   foo_fail: ; false\n\n   # add a tests rule with 3 clauses\n   tests: foo_ok? 'example \"argument\"' ; echo \"foo_ok success\"\n   tests: !foo_ok ; echo \"This is never called, but MAIN still passes\"\n   tests: foo_fail? ; echo \"This is also never called\"\n\n   # add tests to MAIN\n   MAIN: tests\n\n\nRULE FUNCTION\n\n  This is the plain form a rule file is preprocessed into.  A rule file you write is normally\n  written in the make/just-like syntax of RULE SYNTAX above, which is the preferred\n  one; 'rule' statements are for what that syntax cannot express.\n\n   rule [[--rule-flag].. <name>:] [--clause-flag].. [[+][!]deps[?|~][??] args..]..\n   rule [[--rule-flag].. <name>:] [--clause-flag].. [[+][!]deps[?|~][??] args..].. -\n   rule [[--rule-flag].. <name>:] [--clause-flag].. [[+][!]deps[?|~][??] args..].. -- [body..]\n\n  * [--rule-flag]\n\n    Sets flags for a rule. Flags are additive and affect the rule, not single clauses.\n\n    --always\n      The result is not cached and the rule will be always evaluated again.\n\n    --bare\n      When this rule is called as initial rule (bar <rule>) then do not eval SETUP,\n      PREPROCESS, POSTPROCESS and CLEANUP.\n\n    --reverse\n      The clauses of this rule will be evaluated in reverse order\n\n    --disjunct\n      Makes the clauses of this rule disjunctive. The rule will succeed with the first\n      successful clause. Normally clauses are conjunctive and fail with the first failing\n      clause.\n\n    --meta\n      Metarules do not have their own clause local variable. This allow them to modify\n      the clause locals of the calling rule. 'clause_local' for example is a metarule.\n      User defined rules that want to modify clause locals will need to be meta too.\n      See 'try_cargo_toolchain' for an example.\n\n    --parallel\n      Every clause of this rule is evaluated as if it were marked '--parallel' (see the clause\n      flags below), and the dependencies of all its clauses are started at the first of them, so\n      the rule costs its slowest dependency rather than their sum.  The clause bodies still run\n      in the shell of the rule and in declared order: a body works in the directory and may rely\n      on what the body before it did.  Opt-out: a clause marked '--conclusive' or '--default'\n      stays serial.  A '--disjunct' rule keeps its clauses in order -- which clause is tried first\n      is its meaning -- and inherits the flag per clause instead.\n\n  * [<name>:]\n\n    A rule can have a optional name (suffixed with a colon).  When the name is not given it\n    defaults to 'MAIN'.  Rules that have only a name but no dependencies and no body create a\n    body that calls a bash command or function with the same name as the rule passing rule\n    arguments to it.\n\n  * [--clause-flag]..\n\n    Sets flags affecting this clause only.\n\n    --conclusive\n      Makes a clause result conclusive, if not skipped no more clauses will be tried.\n\n    --default\n      Default clauses will be kept at the end of the list of clauses. Using '--default'\n      together with 'disjunct' rules allows one to hook up action while maintaining a fallback\n      or warning/error when no hooked up rule succeeded.\n\n    --parallel\n      Every dependency of this clause which is not a conditional one may be started early, as if\n      it were prefixed with '+'.  Opting out is per clause, so the rule flag '--parallel' sets\n      this for every clause but the '--conclusive' and '--default' ones.  See the dependency\n      rules below.\n\n  * [[+][!]deps[?|~][??] args..]..\n\n    Dependencies, are a list of other rules that this rule depends on. These will be executed\n    in order. If any of the dependencies fails then this rule is considered failed as well, no\n    further dependencies, bodies or clauses are executed unless this dependency is a checking\n    or unconditional one.\n\n    A dependency can be either prefixed with an exclamation mark or suffixed with question\n    marks or a tilde. The exclamation mark prefix expects the dependency to fail and will\n    continue then, if the dependency succeeds then the remaining dependencies and the rule\n    body are skipped. With a question mark as suffix the dependency is expected to succeed,\n    when it fails then the rest of the dependencies and the rule body is skipped. With a tilde\n    as suffix outcome of the dependencny is ignored and the rest of the dependencies and the\n    rule body is executed.  The difference here is that these checking dependencies decide if\n    a clause should be skipped or succeed while a failure on a normal dependency will fail a\n    rule instantly. When a rule is suffixed with two question marks then this behaves like\n    \"'rule_exists? dep' dep\". Making this dependency optional. This can be combined with the\n    another question mark, a tilde, or explamation mark prefix. This is commonly used to\n    define optional rule hooks.\n\n    Dependencies can have arguments. When provided then the dependency and its arguments must\n    be quoted. These arguments are passed in the 'RULE_ARGS' array to the body of the rule.\n\n    A dependency prefixed with '+' is a candidate for starting early: a run of such dependencies\n    is started concurrently ('BAR_PARALLEL' bounds how many run at once, 1 starts none early),\n    while the verdicts of a run which succeeds are still taken in declared order.  A conditional\n    dependency ('?', '??', '!') and a dependency without the prefix end the run and are a barrier:\n    everything started before them is collected before they run, and so is everything started\n    before the body.  '~' is not a barrier, it only discards the verdict, so '+dep~' starts early\n    like any other and a run continues through it.  Whether a rule may be started early is the\n    user's claim that its side effects are acceptable; marking a clause '--parallel' (or its rule)\n    is the shortcut for the prefix on each of its dependencies which is not a conditional one.\n\n    A failure settles the rule wherever it is declared: the clauses which follow are started\n    ahead, so one of them failing ends the rule at once instead of after the clause before it has\n    been collected -- and what the rule started is killed.  Keep-going ('BAR_KEEP_GOING') is the\n    mode which runs the remaining clauses anyway, and a disjunct rule tries them in order, so\n    neither is settled by a failure behind it.\n\n  * -\n\n    When there is a single hypen '-' at the end of a rule definition then the rule body is a\n    call to a command or function of the same name as the rule with all arguments passed. This\n    is used when one wants to add dependencies to a existing command or function.\n\n  * -- [body..]\n\n    After a double hyphen a optional rule body follows. This are the commands to\n    execute for this clause. If these fail then the rule is considered failed. When a name but\n    no body and no dependencies are given then the body defaults to the rule name. This makes\n    it easy to translate simple parameterless commands and functions to rules. Usually the\n    body has to be quoted to prevent shell expansions at definition time. Later when a rule\n    becomes evaluated the body is passed to 'eval'.\n\n    When the body is omitted, then the rule body is read from stdin. This allows to use\n    heredocs or herestrings or read the body from a file.\n\n    When a rule overrides an existing command a 'NOTE' will be printed on higher verbosity\n    levels. The user is responsible for sensible overiding of commands.\n\n  Rules must not have mutually recursive dependencies. When such is detected the\n  evaluation aborts.\n\n  Examples:\n\n   # Add a clause to 'MAIN'\n   rule -- echo hello\n\n   # Different ways to define rule bodies\n   rule foo_ok: -- echo inline\n   rule foo_ok: -- '\n       echo inline quoted\n   '\n   rule foo_ok: -- <<<\"echo herestring\"\n   rule foo_ok: -- <<EOR\n      echo heredoc\n   EOR\n\n   # Make a rule that fails\n   rule foo_fail: -- false\n\n   # functions and commands can be used as rules, this should be preferred\n   function example\n   {\n       echo \"i am example called with $*\"\n   }\n\n   # add a tests rule with 3 clauses\n   rule tests: foo_ok? 'example \"argument\"' -- echo \"foo_ok success\"\n   rule tests: !foo_ok -- echo \"This is never called, but MAIN still passes\"\n   rule tests: foo_fail? -- echo \"This is also never called\"\n\n   # add tests to MAIN\n   rule tests\n\n  For some more examples see the 'example' file that ships with bar.\n\n\n  Special Rules and Names\n\n    Rules that do not have a name fall back to 'MAIN', when no rule name is given at execution\n    time then 'MAIN' will be evaluated.\n\n    If exist 'SETUP', 'PREPROCESS', 'POSTPROCESS' and 'CLEANUP' will be called.\n    * SETUP\n      Should set up the environment for all subsequent evaluation. Rules in 'SETUP'\n      should be self-sufficient and not expect a working environment yet.\n      A failure here will abort bar.\n    * PREPROCESS\n      Doing any preparatory tasks within the set up environment.\n      A failure here will abort bar.\n    * MAIN\n      When the bar is not called via a symlink or the user did not provide a rule to\n      evaluate, then MAIN is the default rule. Otherwise the user supplied or symlink-deduced\n      rulename will be evaluated. The result of this rule determines the exitcode of bar.\n    * POSTPROCESS\n      Is for evaluating things after the main rule finished. The environment and\n      state is still the same as in the main rule. A failure here will be reported but not\n      reflected in the exitode.\n    * CLEANUP\n      Is for removing temporary resources and doing any other kind of cleanup work. This\n      means the state and environment at cleanup time may be differen/partially destructed and\n      should not be relied upon. Clauses in the CLEANUP rule are evaluated in reverse order of\n      their definition. A failure here will be reported but not reflected in the exitcode.\n\n    After a rule that is not flagged with '--always' got evaluated it is not allowed to add\n    more clauses to it anymore, as the outcome would be unexpected and impure.\n\n\n  Keep-Going Mode\n\n    Bar supports a keep-going facility via the BAR_KEEP_GOING environment variable. When\n    enabled, clauses that fail do not immediately halt rule evaluation—subsequent clauses\n    continue to execute, though the overall failure is still propagated.\n\n    BAR_KEEP_GOING can be set to a number which is decremented on each failure, when this\n    reachs zero no more failures are accepted and the evaluation will terminate.\n\n    When BAR_KEEP_GOING set to anything else than a number then evaluation will accept\n    infinite failures.\n\n    When not set then it defaults to one then evaluation will terminate on the\n    first failure.\n\n    Clauses marked '--conclusive' immediately halt keep-going.\n\n\n  Parallel Evaluation\n\n    Parallelism is opt-in, and its three marks are the same start at three scales.  A rule marked\n    '--parallel' starts the dependencies of all its clauses at the first of them -- the bodies stay\n    in declared order, a body works in the directory -- and its clauses inherit the flag: each\n    behaves as if marked '--parallel' but for the '--conclusive' and '--default' ones, which stay\n    barriers.  The inheritance reaches the dependencies those clauses declare, so the whole rule is\n    one run: a dependency which is a '--parallel' rule itself hands the flag to its own dependencies\n    in turn, and the subtree is a pool rather than one job per level.  A clause marked '--parallel'\n    is the same start for the dependencies of that clause only, and the '+' prefix is the same for\n    one dependency: it may be started before its turn, with the run of '+' dependencies beside it\n    evaluated concurrently.  The verdicts of a run which succeeds are taken in declared order.\n\n    What parallelizes anything is one of those marks; 'BAR_PARALLEL' only says how many jobs may run\n    at once.  A rule, clause or dependency which is not flagged runs in serial order however large\n    the budget is.  Unset or empty takes one job per available processor ('nproc'), 1 starts\n    nothing early and bar evaluates exactly as it does serially, and any other value which is not a\n    number is read as 1.  The budget is one for the whole run: a job which starts jobs of its own\n    spends from it, and a shell which has to wait for a slot gives its own back first, so nothing\n    waits on a token it is holding.\n\n    The budget is a GNU make job server, named the way make names it ('MAKEFLAGS',\n    '--jobserver-auth=fifo:<path>'), and it is shared in both directions.  A bar which a 'make -j'\n    starts joins that make's server instead of opening a second budget, so the build stays at make's\n    '-j'; a 'make' a body starts finds this run's server in 'MAKEFLAGS' and spends from it, so it\n    stays at 'BAR_PARALLEL'.  A 'make -j1' has no server to share.\n\n    A failure ends the run at the failure.  A rule which fails starts no further dependency and no\n    further body, and the jobs it has already started are killed: their verdict cannot be used any\n    more.  A dependency is cancelable, the execution of a body is atomic once it has started, and\n    the rule's own bodies have not run -- so nothing of that rule is half-done.  The failure is\n    taken when it arrives, not in the declared order of the dependencies -- a dependency which fails\n    while one declared before it is still running is the rule's verdict, and that one is killed --\n    so a run pays the failure and not the work behind it, wherever the failing dependency was\n    declared.  A rule which stops early on a '--conclusive' clause and *succeeds* collects what it\n    started instead: what a body left behind is the point of a run which succeeds.\n\n    An interrupt ends the run: '^C' from the terminal, or a 'TERM'/'HUP' sent to it.  What the run\n    started is killed with it -- the jobs with the process trees below them, and the children a body\n    left in the background -- because they are the ones which cannot take the signal: an asynchronous\n    command of a non-interactive shell has SIGINT and SIGQUIT ignored, so they would outlive the run\n    which pays for them.  The exit path still runs, and the status is the signal's (130 for '^C').\n\n    A verdict is shared.  A rule which two jobs both need is evaluated once and each of them takes\n    that result, so a diamond costs its arms one evaluation of what they share; the results live in\n    a store of this run, which ends with it, and a rule marked '--always' is never stored.\n\n    A run which succeeds takes its verdicts in declared order, so a checking dependency and the\n    clause body see the same results, in the same order, at any BAR_PARALLEL; a failure is the\n    exception above, and a killed job records no verdict at all.  A job's own output is not\n    serialized: two bodies writing to the same terminal interleave, and bar's progress lines may\n    arrive out of order.  A notice is cleared in place rather than ended, so a shell whose line\n    another already finished leaves no empty one behind it.\n\n\n  Purity Requirements\n\n    All rules and functions must be sequentiually pure in their success branch. This means\n    side effects must not alter the behavior of subsequent rules when they fail. For rules\n    where this may be the case, like rules changing directories they must rather die than\n    proceed with a undefined state in case of a failure.\n\n\nRULE ENVIRONMENT\n\n  Rules implicitly depend on the arguments passed and the current directory they run in. Any\n  other state or environment variable is not considered. When the result of a rule would\n  depend on such external state then the rule is impure and may lead to wrong\n  results.\n\n\nMODULES\n\n  Modules are shell snippets normally located in '$BAR_DIR' ('Bar.d/', 'bar.d/', '.Bar.d/' or\n  '.bar.d/') when called as 'bar' or symlink or '~/.config/makeitso' when called as 'makeitso'.\n  The 'std_lib' and 'rules_lib' are always loaded at startup. Modules matching '*_rules' are\n  also autoloaded at startup. Any other '*_lib' must be manuallly loaded with 'require'.\n  Modules with simple alphabetic names without underscores are lazy loaded on demand.\n\n  Modules are loaded with the 'require' function. This ensures that they are loaded only once.\n  Require will load modules outside of '$BAR_DIR' when a path containing at least one slash\n  is given.\n\n  The first variant for modules that are initially loaded is used for modules that define\n  helper functions that don't have associated rules or rules that are primarly used for\n  checking conditions before delegating to the actual rules which do the work.\n\n  The second variant is for mudules that define rules which are self-contained and can't\n  extend existing rules with new clauses. Here are the rules and functions which do the actual\n  work.\n\n  The reason for this is that some will add clauses to existing rule which must be done before\n  the rules are evaluated while we can improve performance by lazy loading modules that are\n  not always needed.\n\n  The module loader derives the module name from the rule name by removing any prefixes and\n  suffixes. Thus the rules which shall trigger loading must follow the naming schema with the\n  module name after the prefixes in the rule name.\n\n  Per convention '<name>_rules' is a module that contains the rules (and few functions) that\n  that add clauses to other rules to make 'name' accessible. These '*_rules' files are\n  autoloaded at startup.\n\n  '<name>_lib' are libraries for support functions other modules may use (with 'require\n  name_lib').\n\n\nAUTOMATIC MODULE LOADING\n\n  Rule names may include underscores, then the word before the first underscore is used to\n  determine a module name used for auto-loading rules. They can have special prefixes which\n  are removed when auto-loading:\n\n  - 'is_', 'has_' and 'try_' are reserved for check rules they don't have any special\n    semantic except for being stripped at auto loading.\n\n  When a rule is not found then a module name is derived from the rule name by removing the\n  'try_', 'is_', 'has_' prefixes and cutting off anything behind the first\n  underscore. Eg. 'try_module_check' results in 'module'. This is then searched as\n  'Bar.d/module', 'bar.d/module', '.Bar.d/module' or '.bar.d/module' (or respective makeitso\n  variants) and loaded if present. Thus modules that are automatically loaded must be named by\n  single word without underscores.\n\n\nSTATE DUMP\n\n  Loading the modules and the rulefile is most of bar's startup cost and only static\n  preprocessing.  To optimize this bar writes a dummp of that state to '.bar.<rulefile>.dump'\n  beside the rulefile: 'Barf' gives '.bar.Barf.dump', 'makeitso SomeOtherRules' gives\n  '.bar.SomeOtherRules.dump'.  Running bar again uses this state file when no rules have\n  changed instead of preprocessing all files again.  'bar init' puts the file into\n  '.gitignore'. 'BAR_STATE_DUMP' names another file, 'off' in the environment or as a line of\n  the rulefile turns this optimization off.\n\n\nHELPER REPERTOIRE\n\n  Many recurring questions like filters, retrieval, listing, counting, a state queries, have\n  one deterministic answer which would otherwise be re-derived with several commands and a lot of\n  context.  Such an answer is a 'helper': a rule of a 'helper module' which names itself with a\n  '# helper-module:' header in its first lines and registers each of its rules with a\n  '# helper:', '# helper-bar:' or '# helper-makeitso:' declaration above it, the three forms\n  share their four fields and differ only in which personality ('bar' or 'makeitso') lists the\n  helper, and every listed row is prefixed with that personality.\n\n   bar helpers          # the repertoire of this installation, one line per helper plus its count\n   bar helpers --json   # the same repertoire as one JSON object, for a wrapper\n   bar helpers --check  # verify that every helper module is registered (also run by 'lints')\n\n  The repertoire is generated by reading the module sources: listing it loads no module and runs\n  no helper, and 'makeitso helpers' works from any directory. It even appends the 'bar'\n  repertoire of the directory it is run in (a 'bar helpers' there), again without dedup.  See\n  'Bar.d/helpers_rules' for the naming rules (a helper rule loads its module by its own first\n  word), the registration format, the output conventions and the elision and exit code\n  requirements every helper has to follow.\n\n\nSTANDARD RULES\n\n  Bar ships with a module that defines a set of standard rules where other modules can add\n  clauses to. Check 'Bar.d/std_rules' for details.\n\n  'SETUP', 'PREPROCESS', 'MAIN', 'POSTPROCESS' and 'CLEANUP' are special rule names called\n  approbiately.\n\n  The 'clause_local [var[=value]|=var..]' is for defining variables that are local to a clause.\n  Only scalars can be clause local.  '<var>=<value>' sets the variable to that value, '<var>=' sets\n  it to the empty string, and '=<var>' keeps the value the outer scope had.  The clause ends with\n  the outer value back in place.\n\n\nMODULE API/RULES\n\n  agent_rules - The agent output contract -- a run reduced 'up to the point'\n\n    bar is written for humans: a run prints everything every tool produced.  In a terminal that is\n    right, in an agent's context window it is fatal -- a full 'bar tests' here is ~400 lines /\n    16 kB of which the actionable part is a handful of lines.\n\n    This module turns a *run* into a small, decision shaped *answer*.  Three levels of progressive\n    disclosure, the caller picks the level:\n\n      bar verdict <target>..         one line per target                      (polling, gating)\n      bar check   <target>..         verdict + chain + totals + bounded, line numbered evidence + log path\n      bar log     <target> [range]   the untruncated raw log, on demand\n\n    The contract ('never lie') is:\n\n      * the verdict line is printed always, for every target, at every verbosity -- never filtered,\n        never truncated;\n      * on failure the exact totals (lines and bytes) of the log are always printed, even when\n        there is no evidence to show at all;\n      * the raw log is written to disk before anything is printed and its absolute path is printed\n        on failure.  It is kept for successes too, so 'bar log' always works;\n      * only bar's own machine emitted records ('OK:' 'FAIL:' 'CHECK:' 'RUN:' 'EVAL:') are pattern\n        matched.  Foreign tool text is *never* filtered by keyword -- doing exactly that is how the\n        real shellcheck findings got lost once -- it is bounded *positionally* (a head and a tail\n        window) and the elision is announced with the exact log line range that was left out;\n      * every evidence line carries its line number in the log, so 'bar log <target> <n>' (or\n        'sed -n <n>p <log>') retrieves that line again, byte for byte;\n      * a failure is never masked: the exit code is the rc of the first failing target;\n      * bar's own options are never run as targets either.  Everything after a rule name belongs to\n        that rule, so 'bar check agent --quiet' hands the option to 'check' -- it is dropped (with a\n        NOTE from verbosity 3 on) instead of being reported as the phantom result 'OK --quiet'.  A\n        target whose *name* begins with a dash stays reachable behind a bare '--': 'bar check --\n        --weird' runs the target '--weird'.\n\n    Measured on bar itself: 'bar tests' 16 kB / 409 lines -> 'bar check tests' 9 B / 1 line\n    (success), and a 401 line failure of 14,6 kB -> ~1 kB carrying the verdict, the failing chain,\n    the totals and the line that holds the signal.\n\n    Exit codes: 0 when every target passed, 2 on a usage error, otherwise the rc of the first\n    failing target propagated verbatim (3, 128+signal, ...).  'bar log' answers 3 when no log is on\n    record for the target.\n\n    The log is a *raw* capture: when the invoking stderr was a terminal, bar's own record lines in\n    it carry that terminal's escape sequences.  The printed evidence never does (it is normalized)\n    and the totals always describe the file exactly as it is on disk.\n\n    Variables:\n\n      BAR_AGENT_DIR (gx)\n        Directory for the run logs below $BAR_TOPLEVEL.\n        Default: ${BAR_AGENT_DIR:-.bar.agent}\n\n      BAR_AGENT_INNER (gx)\n        Verbosity the measured run is forced to.\n        Default: ${BAR_AGENT_INNER:-2}\n\n      BAR_AGENT_HEAD (gx)\n        Evidence lines taken from the head of the log.\n        Default: ${BAR_AGENT_HEAD:-8}\n\n      BAR_AGENT_TAIL (gx)\n        Evidence lines taken from the tail of the log.\n        Default: ${BAR_AGENT_TAIL:-4}\n\n      BAR_AGENT_MAX_LINE (gx)\n        Maximum bytes of a single evidence line.\n        Default: ${BAR_AGENT_MAX_LINE:-384}\n\n      BAR_AGENT_MAX_BYTES (gx)\n        Maximum bytes of a printed failure block.\n        Default: ${BAR_AGENT_MAX_BYTES:-4096}\n\n      BAR_AGENT_KEEP (gx)\n        Number of runs (log directories) to keep.\n        Default: ${BAR_AGENT_KEEP:-5}\n\n      BAR_AGENT_JSON (g)\n        When set, print one JSON object per run.\n        Default: ${BAR_AGENT_JSON:-}\n\n    Functions:\n\n      agent_run_dir - create this run's directory, prune old ones, update 'latest'\n        The directory is named after the timestamp of this invocation and the tree it runs in, the\n        same way memodb names its trees.  'latest' is a convenience symlink for humans, it is\n        explicitly *not* a way to find a log (see agent_find_log).\n\n      agent_tree_id - identifies the tree this run is made in\n\n      agent_keep - BAR_AGENT_KEEP, clamped to at least one\n\n      agent_gc - keep the newest BAR_AGENT_KEEP run directories, delete the rest\n        Only directories that look like a run directory ('<timestamp>-<tree>') are candidates, and\n        only below our own run directory, so 'latest' and anything else is never touched.  A failure\n        to prune is not a failure of the run that happens to trigger the prune.\n\n      agent_find_log <target>\n        newest log for <target>, newest run first\n\n      agent_strip_ansi <text>\n        remove terminal control sequences from a single line\n        For the few lines bar interprets itself; the bulk of the normalization happens in\n        agent_evidence.\n\n      agent_fail_chain <target> <log>\n        the failing chain, target plus bar's own records\n        bar print a ' FAIL: <rule>: <dependency>' record when a *dependency* fails, but a clause\n        *body* that fails just returns -- a target failing on its own therefore leaves no record at\n        all.  The chain always starts at the target and is extended by the records found in the log,\n        capped at four of them (the chain is part of the block budget).\n\n      agent_evidence <log> <total-lines> <budget-bytes>\n        bounded excerpt as 'E'/'X' records\n        Streams the log once and prints at most BAR_AGENT_HEAD + BAR_AGENT_TAIL evidence lines as\n        'E <log-line-number> <text>' records, preceded by one 'X <from> <to>' record when lines are\n        elided ('X 0 0' means there is no evidence at all).\n        Evidence is *selected positionally*: the first and the last lines of the log that are\n        neither bar's own records nor empty.  No keyword decides what an agent gets to see.  Lines\n        longer than BAR_AGENT_MAX_LINE are clipped at a word boundary with the number of bytes left\n        out appended in '<+NB>', so nothing is hidden.  The scan streams: memory and output stay\n        bounded for arbitrarily large logs.\n\n      agent_rc_text <rc>\n        printable rc, '?' when it is not a number\n\n      agent_run_target <target> <dir>\n        run one target, write its merged log, print its rc\n        The target runs in a subshell with stdout and stderr merged into one log, so the log keeps\n        the order in which the tool wrote it (memodb keeps the two streams in separate files and\n        loses that order).  The subshell also contains a target whose clause body calls 'exit' --\n        that would otherwise take the whole report down with it -- and hands back its real rc.\n        The verbosity of the inner run is pinned so the records bar writes into the log do not\n        depend on how 'bar check' itself was called, and the output format of the outer invocation\n        is dropped: a target that calls bar itself (a project checking its own checks) must not\n        suddenly print JSON just because 'bar --json check <target>' was used.\n\n      agent_max_block - bytes of BAR_AGENT_MAX_BYTES that may go into evidence\n        The block carries a fixed part (verdict, chain, totals, log path) and the elision marker;\n        both are reserved before the evidence budget is handed to agent_evidence.\n\n      agent_excerpt_print <log> <total> <budget>\n        render the excerpt of agent_evidence\n\n      agent_report_failure <target> <log> <rc>\n        print the bounded failure block\n\n      agent_json_evidence <log> <total> <budget>\n        the evidence and elision members\n\n      agent_json_target <target> <log> <rc>\n        one JSON object for a measured target\n        The same fields the text block carries: rc, chain, the byte exact totals and the log path,\n        plus the bounded evidence.  Consumers that need a parser get one; the terse text stays the\n        default because an agent reads lines, not braces.\n\n      agent_json_success <target> <log>\n        one JSON object for a target that passed\n\n      agent_run_block <mode> <target>..\n        body of the 'check' and 'verdict' rules\n        <mode> is 'verdict' (one line per target, plus the log path on failure) or 'check' (the\n        full bounded failure block).  Nothing changes the verdict itself: that line is always\n        printed, at every verbosity.\n        Everything after the rule name belongs to the rule, so bar's own options land here when they\n        are written *after* the target ('bar check agent --quiet').  Such an argument is not a\n        target: running it would print a verdict line for it, and a consumer reads 'OK --quiet' as a\n        measured result -- a lie shaped line in the one surface that must never lie.  Dashed\n        arguments are therefore dropped (a NOTE from verbosity 3 on names them), and a bare '--'\n        turns the dropping off again, so a target whose name starts with a dash stays reachable:\n        'bar check -- --weird'.\n\n      agent_log_body <target> [<from>,<to>|tail:<n>]\n        print the raw log of a previous run\n\n    Rules:\n\n      check\n        Run targets and print a bounded verdict block: one 'OK <target>' line when a target passed,\n        otherwise its verdict, the failing chain, the exact totals of its raw output, a bounded and line\n        numbered excerpt of it and the path of the untruncated log.  Exits with the rc of the first\n        failing target.  Only targets are run: an argument that begins with a dash -- bar's own option\n        written after the target, 'bar check agent --quiet' -- is not one and is dropped with a notice\n        from verbosity 3 on.  Write the option before the rule ('bar --quiet check agent') or a target\n        whose name begins with a dash behind a '--' ('bar check -- --weird').\n\n      verdict\n        Run targets and print one line per target only ('OK <target>' / 'FAIL <target> (rc=..)').  The\n        cheapest form, meant for polling loops and gating conditions; a failure additionally gets the\n        path of its log so it can be retrieved with 'bar log'.  The same argument handling as for\n        'check': a dashed argument after the target is dropped, never reported as a target of its own,\n        and a '--' makes everything after it a target again.\n\n      log\n        Print the raw log a previous 'bar check'/'bar verdict' produced for <target>: everything, a line\n        range <from>,<to>, or 'tail:<n>'.  Byte identical to what 'sed -n'/'tail -n' print.\n\n      --json\n        Machinereadable output: emit one JSON object per run (targets, rc, totals, evidence) instead of\n        the terse text.  For wrapper scripts, not for reading.\n\n  cargo - Rust/cargo support\n\n    Variables:\n\n      CARGO_TOOLCHAIN (xg)\n        The rust toolchain that is used for most operations.\n        Default: ${CARGO_TOOLCHAIN:-}\n\n    Functions:\n\n      is_cargo_tool_installed [+toolchain] <tool> [args..]\n        Checks if 'cargo <tool>' is installed\n        When [args..] is not present then '--version' is used. Tools that do not handle\n        '--version' need 'args..' to give some sensible result.\n\n      is_cargo_toolchain_available <+toolchain>\n        Check if a toolchain is available\n\n      cargo_tool_complete List available cargo tools/subcommands for completion\n        This function provides completion for cargo tool names (subcommands).\n        It lists both built-in cargo commands and installed extensions.\n\n      cargo_fix Run 'cargo fix'.\n        Note that this will modify the source in place.\n\n      cargo_build [buildargs..]\n        Compile a local package and all of its dependencies\n\n      cargo_test [testargs..]\n        Execute all unit and integration tests\n\n      cargo_check Run 'cargo check'. Testing whenever the source can be compiled.\n\n      cargo_clippy_errors Tests with 'cargo clippy' for errors only\n\n      cargo_clippy_strict Test with 'cargo clippy' for warnings and errors\n\n      cargo_doc [docargs..]\n        Build this package's and its dependencies' documentation\n\n      cargo_update Run 'cargo update'\n\n      cargo_has_unsafe_code Checks if the source use 'unsafe' code.\n        This does only a coarse '-Funsafe-code' check, but does not depend on external tools.\n\n      cargo_miri [miriargs..]\n        Run tests under miri (memory sanitizer)\n\n      cargo_bench Run 'cargo bench'\n\n      cargo_publish Run 'cargo publish'\n\n    Rules:\n\n      cargo_lint\n        Runs all linters\n\n      cargo_toolchain <+toolchain>\n        Clause local change of the toochain used by cargo.\n\n      cargo_fmt_check\n        Checks whenever the source code is well formatted.\n        If available the '+nightly' toolchain is used.\n\n      cargo_build_unit_tests\n        Builds unit tests.\n\n      cargo_test_units\n        Runs unit tests.\n\n      cargo_build_integration_tests\n        Build integration tests\n\n      cargo_test_integrations\n        Run integration tests. This includes the doctests.\n\n      cargo_fmt\n        Run 'cargo fmt'. When available the '+nightly' toolchain is used.\n        Note that this will modify the source in place.\n\n      cargo_miri\n        When there is unsafe code then run a test under miri supervision.\n        Requires the '+nightly' toolchain.\n\n      cargo_msrv_check\n        Checks whenever the 'rust_version' in the manifest is sufficient.\n        This check only happens when 'cargo-msrv' is installed, otherwise a warning is printed.\n\n      cargo_mutants\n        Run 'cargo mutants' when it is available.\n        When 'cargo-mutants' is not installed a warning is printed.\n\n      cargo_outdated\n        Run 'cargo outedated' when it is available.\n        When 'cargo-outdated' is not installed a warning is printed.\n\n      cargo_audit\n        Run 'cargo audit' when it is available.\n        When 'cargo-audit' is not installed a warning is printed.\n\n      cargo_semver_checks\n        Run cargo semver-checks to test if a semver for a release is sufficient\n\n  cargo_rules - General support rules for Rust/Cargo projects\n\n    Checks whenever we are in a cargo project.\n    Hooks cargo support into the 'std_rules'\n\n    Functions:\n\n      has_cargo_manifest Checks if 'Cargo.toml' exists.\n\n      has_cargo_lock Checks if 'Cargo.lock' exists.\n\n    Rules:\n\n      is_cargo_installed\n        Successful when 'cargo' is available.\n\n      is_cargo_project\n        Check whenever we are ready to use cargo here.\n\n      release_publish\n        Push a software release to crates.io (or whatever registry is configured).\n\n  cliff - git cliff changelog generation\n\n    Variables:\n\n      GIT_CLIFF_OUTPUT (gx)\n        Output where to write the changelog\n        Default: ${GIT_CLIFF_OUTPUT:-CHANGELOG.md}\n\n    Functions:\n        The generated changelog and cliff.toml (when there where changes) are implicitly staged.\n        This does not git-commit though.\n\n    Rules:\n\n      cliff_changelog [cliff_args..]\n        use git cliff to generate a changelog.\n\n      cliff_release_changelog\n        Makes a changelog for a release, commits and tags it. Must be called from a release worktree\n        with state 'tested'.\n\n      cliff_changelog_amend\n        Regenerates the changelog up to the previous commit and amends the current commit with the new changelog\n\n  cliff_rules - Hooks git cliff into build_versioned_artifacts\n\n    Rules:\n\n      release_changelog\n        Create a cliff changelog for releases when there is a 'cliff.toml' at the project root.\n\n  entr - Support for the entr file watcher\n\n    Functions:\n\n      entr_watch_git_index\n        Runs bar in a loop again whenever the git index gets changed. Takes a target rule and its parameters as argument. Defaults to 'MAIN'.\n\n    Rules:\n\n      is_entr_installed\n        Succeeds when 'entr' is available.\n\n      entr_watch\n        Runs bar in a loop again whenever a file gets changed.\n        Takes a target rule and its parameters as argument.\n        Defaults to 'MAIN'.\n\n  entr_rules - hooks entr into the watch/watch_git_index rules\n\n  git - Git support rules\n\n    Rules:\n\n      is_git_repository\n        Succeeds when we are in inside (at or below) a git repository.\n\n      is_git_toplevel\n        Succeeds when we are at the top level of a git repository.\n\n      git_is_clean\n        Checks if there are modified files, fails if there are any.\n\n      git_branch_matches [branchpattern..]\n        Checks that the current git branch matches any of the given patterns.\n\n      is_git_pre_commit_hook\n        Checks if called as githook.\n\n      is_git_pre_merge_commit_hook\n        Checks if called as githook.\n\n      is_git_prepare_commit_msg_hook\n        Checks if called as githook.\n\n      is_git_commit_msg_hook\n        Checks if called as githook.\n\n      is_git_applypatch_msg_hook\n        Checks if called as githook.\n\n      is_git_pre_applypatch_hook\n        Checks if called as githook.\n\n      is_git_post_applypatch_hook\n        Checks if called as githook.\n\n      is_git_post_commit_hook\n        Checks if called as githook.\n\n      is_git_pre_rebase_hook\n        Checks if called as githook.\n\n      is_git_post_checkout_hook\n        Checks if called as githook.\n\n      is_git_post_merge_hook\n        Checks if called as githook.\n\n      is_git_pre_push_hook\n        Checks if called as githook.\n\n      is_git_pre_receive_hook\n        Checks if called as githook.\n\n      is_git_update_hook\n        Checks if called as githook.\n\n      is_git_proc_receive_hook\n        Checks if called as githook.\n\n      is_git_post_receive_hook\n        Checks if called as githook.\n\n      is_git_post_update_hook\n        Checks if called as githook.\n\n      is_git_reference_transaction_hook\n        Checks if called as githook.\n\n      is_git_push_to_checkout_hook\n        Checks if called as githook.\n\n      is_git_pre_auto_gc_hook\n        Checks if called as githook.\n\n      is_git_post_rewrite_hook\n        Checks if called as githook.\n\n      is_git_sendemail_validate_hook\n        Checks if called as githook.\n\n      is_git_fsmonitor_watchman_hook\n        Checks if called as githook.\n\n      is_git_post_index_change_hook\n        Checks if called as githook.\n\n      is_git_main_branch\n        Checks if the current branch matches 'main master'.\n\n      is_git_release_branch\n        Checks if the current branch matches 'release-* release/*'.\n\n      is_git_devel_branch\n        Checks if the current branch matches 'devel-* devel/*'.\n\n      is_git_feature_branch\n        Checks if the current branch matches 'feature-* feature/*'.\n\n      is_git_bugfix_branch\n        Checks if the current branch matches 'bugfix-* bugfix/* fix-* fix/*'.\n\n      is_git_hotfix_branch\n        Checks if the current branch matches 'hotfix-* hotfix/*'.\n\n      is_git_improvement_branch\n        Checks if the current branch matches 'improvement-* improvement/*'.\n\n      is_git_doc_branch\n        Checks if the current branch matches 'doc-* doc/*'.\n\n      is_git_wip_branch\n        Checks if the current branch matches 'wip-* wip/*'.\n\n      is_git_experimental_branch\n        Checks if the current branch matches 'experiment-* experiment/*'.\n\n  git_lib - Git support library\n\n    Functions:\n\n      git_toplevel\n        Returns the path to the toplevel git repository.\n\n      git_bar\n        Returns the path to the bar that is initialized in the git toplevel. Fails when there is no bar initialized.\n\n      git_dir Returns the absolute path to the 'GIT_DIR' (.git/).\n        Note that this may be distinct from '$(git_toplevel)/.git/' when worktrees or other git\n        features are used.\n\n      git_ls_files [git-ls-files-opts]..\n        Sorted list of files in the git repository.\n        git-ls-files '--exclude-standard' is always implied. Use '-c' or '--cached' to list\n        tracked files, '--others' to show untracked files, or both for all files.\n\n      git_branch_name Returns the branch name that is checked out, dies when not in a git branch.\n\n      git_branch_complete List all git branches for completion\n        This function is called by bar_complete to provide completions for branch parameters.\n\n      git_branch_find [patterns]..\n        lists branches matching the given patterns\n\n      git_branch_find_one [patterns]..\n        lists the matching the given patterns, fails when there are more than one\n\n      git_is_ancestor [patterns]..\n        checks if the current branch is a ancestor of the one branch matching the patterns\n\n      git_tree_hash [git-ls-files-opts]..\n        Returns a sha1 hash over the current directory\n        This hash is a identifier used by bar. It is not the same hash git using for storing trees.\n\n      git_add_ignore [patterns..]\n        Adds new ignore patterns to '.gitignore'.\n        Patterns that are already present are skipped.\n\n      git_parse_worktrees [main [dirs [branches [heads]]]]\n        Parses git worktrees into variables provided by the caller\n        - main:     variable storing the main worktree (dir)\n        - dirs:     associative array storing 'branch:directory'\n        - branches: associative array storing 'directory:branch'\n        - heads:    associative array storing 'directory:head'\n\n      git_search <file> [[directory] branch] [--options..]\n        Lists the commits whose tree holds a file with the object id of <file>.\n        <file> is hashed in the caller's directory, before any directory is entered.  Its path -- the\n        prefix of the project which contains it, plus its own name -- is the path the destination\n        filters compare; a <file> outside any project has no prefix and stands by its name alone.\n        <directory> (default '.') is the project whose history is read, <branch> (default: the branch\n        checked out there) the line which is walked, newest commit first.\n        --exact|--name|--dir|--match <glob> - only commits holding the blob at that path, at any path\n        of that name, in that directory, or at a path matching <glob>.\n        --added - only the commits which start a run of presence.\n        --last - only the commits which end one: the last commit in which it was still present.\n        --removed - only the commits right after a run: where it disappeared.\n        --absent - only the commits right before a run: the absent side of an add.\n        --head <N>|--tail <N> - the first N, the last N of the walk; both: the last <tail> of the first <head>.\n        --filter <rx> - only commits whose message header matches the extended regex <rx>; a malformed one is a usage error.\n        --format <format> - print git's own --format; '%H ' is prepended internally and cut off again.\n        --json - a JSON array of commit, date, committer and subject; an empty selection is '[]'.\n        --check - print nothing, succeed only while a commit is left.\n\n  git_rules - Standard rules for git support\n\n    Rules:\n\n      pre-commit\n        Entry rule when called as githook.\n\n      pre-merge-commit\n        Entry rule when called as githook.\n\n      prepare-commit-msg\n        Entry rule when called as githook.\n\n      commit-msg\n        Entry rule when called as githook.\n\n      applypatch-msg\n        Entry rule when called as githook.\n\n      pre-applypatch\n        Entry rule when called as githook.\n\n      post-applypatch\n        Entry rule when called as githook.\n\n      post-commit\n        Entry rule when called as githook.\n\n      pre-rebase\n        Entry rule when called as githook.\n\n      post-checkout\n        Entry rule when called as githook.\n\n      post-merge\n        Entry rule when called as githook.\n\n      pre-push\n        Entry rule when called as githook.\n\n      pre-receive\n        Entry rule when called as githook.\n\n      update\n        Entry rule when called as githook.\n\n      proc-receive\n        Entry rule when called as githook.\n\n      post-receive\n        Entry rule when called as githook.\n\n      post-update\n        Entry rule when called as githook.\n\n      reference-transaction\n        Entry rule when called as githook.\n\n      push-to-checkout\n        Entry rule when called as githook.\n\n      pre-auto-gc\n        Entry rule when called as githook.\n\n      post-rewrite\n        Entry rule when called as githook.\n\n      sendemail-validate\n        Entry rule when called as githook.\n\n      fsmonitor-watchman\n        Entry rule when called as githook.\n\n      post-index-change\n        Entry rule when called as githook.\n\n      release_publish\n        Push tags and branches to the default push remove when publishing a release.\n\n  githook - Support library for managing githooks\n\n    Rules:\n\n      githook_enable\n        Takes a list of githooks to enable as parameters. Must be called from the git_toplevel.\n        Fails when a hook is not available.\n\n      githook_disable\n        Takes a list of githooks to disable as parameters. Must be called from the git_toplevel.\n        Fails when a hook is not pointing to bar.\n\n  help - Provides help, extracts documentation from modules\n\n    Inline documentation uses two or three hash characters. Three '###' is used for file level\n    documentation to describe a file entirely. Two '##' are used in different contexts:\n\n    Variable definitions that use the 'declare' keyword can use '##' at the end of the same\n    line to document the variable.\n\n    Functions use formal parameter syntax: 'function name ## [--opt] <required> [optional] - description'\n    Parameters use: <mandatory>, [optional], param.., [param..], --flags, foo|bar alternatives.\n    Multi-line documentation continues on following lines starting with optional whitespace\n    followed by '##' and parameter explanations.\n\n    Rules are documented by '##' lines before the rule definition, using the same formal\n    parameter syntax when the rule accepts parameters.\n\n    Only variables, functions and rules that have doc comments are included in the output.\n\n    Documentation says what a thing does and how to use it.  The reasoning behind it, why it is\n    written that way, what was measured, belongs in single '#' source comments, which are not\n    published.\n\n    Functions:\n\n      help_usage <name>\n        the builtin help of one rule, function or variable, without the whole documentation\n        'bar help <name>' renders every module before it picks the section asked for, which costs\n        seconds on an installation of this size.  This answers it from the one module the name can\n        live in, resolved the way 'rule_autoload' resolves it: 'always_', 'is_', 'has_' and 'try_'\n        are stripped, the word before the first '_' is the module, and '<module>', '<module>_lib'\n        and '<module>_rules' are the files read.\n        A name the narrowed list does not carry falls back to the full documentation (a helper\n        whose module the name does not derive, e.g. 'is_command_installed': 'command' is no\n        module), so the answer stays right and only its cost is that of 'bar help'.\n\n      help_usage_error <name> [reason]\n        a usage error on stderr: what was wrong, and the command which answers the help for name\n\n      help_render_annotated <span>\n        print the render with the source of every block named above it\n        The form 'help --debug' prints: each block is preceded by '#<file>:<line>', the line the text\n        comes from.  It is a wrapper so the annotation is a property of the print, never of the index.\n\n      help_render_print <first> <last>\n        print the rendered lines <first>..<last>, annotated when 'HELP_ANNOTATE' is set\n        A line which carries an origin is preceded by '#<file>:<line>', the form 'help --debug'\n        prints and 'help_lint --debug' reads.\n\n    Rules:\n\n      help [[-s|--short] topic|rule|function|variable|text]\n        Show help topics\n        - [-s|--short]:                 Show only the queried sections\n        - topic|rule|function|variable: Navigate to or show only specified topics\n        - text:                         Search the specified text in the documentation\n        A name also arrives at the topic which documents it, the plural included: 'bar help\n        helpers' starts at HELPER REPERTOIRE, not at the block of the function of that name.\n\n      help_lint [-C <n>] [--rule <id>,..] [--debug] [--width <n>] [-v|--verbose] [file]\n        report formatting violations in the help output\n        Checks rendered documentation -- the text 'bar help' prints, or <file> -- against the rules\n        below, one violation per line as '<line>:<id>'.  Nothing is hooked into 'lints'; a caller\n        runs it.  '--rule' keeps the named ids, '--debug' names the source ('<file>:<line>', from the\n        annotations 'help --debug' prints) instead of the rendered line, '-C <n>' prints n lines of\n        context around each violation, and '--width <n>' also reports lines rendered wider than <n>.\n        '--verbose' is the human form: it appends the rule's own sentence, as the list below spells\n        it, and uses '-C 1' unless a '-C' is given.  Exit 0 when nothing is reported, 1 when something\n        is.\n        A list item is a line whose text starts with '*', a run of them, a single '-' or '+', or a\n        dotted number ('1.'); its text column is what L3 and L4 measure.\n        The rules:\n        A1  the abstract, up to the first column-0 title, is freeform and not checked\n        H1  a section title (column 0) is ALL CAPS\n        H2  a section title does not end in '.' or ';' (an indented line which does is prose)\n        H4  a title is followed by one empty line\n        H5  a section title is preceded by two empty lines\n        B1  blocks are separated by one empty line (two only before a section title)\n        W1  with '--width': the rendered line is not wider\n        W2  an empty line carries no spaces\n        W3  a line carries no trailing spaces\n        T1  no tab, the render is indentation\n        I1  a line indented by an odd number of spaces starts a code block after an empty line\n        C2  a code block is followed by an empty line\n        L3  the text of a list item starts at an even column\n        L4  a continuation line of a list item is indented to that column\n        K1  a column of a term list starts at an even column\n        K2  the columns of one term list align\n        Not checked: markup ('*bold*', '/italic/', '_underline_', quoted code) is allowed and passes,\n        and the abstract.\n\n  helpers_rules - The helper repertoire: deterministic answers to recurring retrieval questions.\n\n    A *helper* is a bar rule which answers one recurring, semi deterministic question -- a filter,\n    a retrieval, a listing, a count, a state snapshot -- in a single invocation, with a bounded\n    and stable output.  Its consumer is an agent: with a helper the question costs one command and\n    a few lines of context, without it several turns of ad hoc pipelines.  Helpers filter, count\n    and quote, they never summarise and never judge.  Where a question is naturally a *check*\n    (pass or fail), it is written as a check target and reported by 'bar check'\n    (Bar.d/agent_rules) instead of being squeezed into a helper.\n\n      bar helpers              the repertoire: one line per helper, then the total count\n      bar helpers --json       the same repertoire as one JSON object (same count)\n      bar helpers --check      verify that every helper module is registered (see below)\n      makeitso helpers           as 'bar', plus the repertoire of the current directory\n\n    The repertoire is read out of the module sources: nothing is loaded and no helper is executed\n    to list it, and 'makeitso helpers' works from any directory.  Which helpers are listed depends\n    on the *personality* bar was called by -- 'bar' or 'makeitso' (see REGISTRATION below).\n\n    HELPER MODULES AND RULE NAMES\n\n    The helpers of one domain live together in one *helper module*, 'Bar.d/<word>', which names\n    itself in its file header:\n\n      # helper-module: <word>\n\n    Every rule of a helper module is named '<word>' or '<word>_<rest>', because bar derives the\n    module to autoload from the word before the first underscore of a rule name ('always_',\n    'is_', 'has_' and 'try_' are stripped first, see 'rule_autoload' in Bar.d/std_lib).  A helper\n    rule therefore loads exactly its own module: invoking one helper never loads another helper\n    module, and listing the repertoire loads none at all.  '<word>' must be a word no other module\n    claims -- bar ships 'git', 'rad', 'cargo', 'net', 'ssh', 'testdir', ... -- because a\n    rule named 'rad_state' would autoload 'Bar.d/rad' and then fail to exist.  Pick a free word.\n\n    Everything in a helper module that is not an entry point is a *function*: functions are not\n    rules, the autoloader never looks for them and the repertoire never lists them.\n\n    REGISTRATION\n\n    A helper registers itself with one declaration line at column 0, directly above its rule\n    definition (only comments and blank lines in between, in the same file):\n\n      # helper:        <name> | <purpose> | <invocation> | <output shape>\n      # helper-bar:    <name> | <purpose> | <invocation> | <output shape>\n      # helper-makeitso: <name> | <purpose> | <invocation> | <output shape>\n\n      <name>        the rule name: '<word>' or '<word>_<rest>' of that module\n      <purpose>     one line, the question it answers -- this is how an agent picks a helper\n      <invocation>  how to call it, its options included ('hermeskanban [--json] <selector>')\n      <output>      one line, the shape of the answer\n\n    The three forms share the four fields and the adjacency rule and differ only in *which\n    personality* lists the helper.  A plain '# helper:' is generic and is listed by both; a\n    '# helper-bar:' is listed only when bar was called as 'bar', a '# helper-makeitso:' only when\n    it was called as 'makeitso'.  Every listed row carries the personality: the invocation field is\n    prefixed with it, so 'bar helpers' prints 'bar hermeskanban [--json]' and 'makeitso helpers'\n    prints the same 'makeitso hermeskanban [--json]', and each JSON object carries that name in a\n    'command' field.  The kind never weakens the guard: an adjacency, naming, unusable-field or\n    duplicate violation is reported for every declaration no matter which personality it addresses.\n\n    'makeitso helpers' also runs 'bar helpers' in the current working directory and appends those\n    rows, each labelled with the 'bar' personality -- the vendored or project repertoire of the\n    place it is run in.  There is no dedup: a generic '# helper:' is then listed twice, once as\n    the site's 'makeitso' helper and once as the directory's 'bar' helper, and the two may even run\n    different code.  The count line still counts every listed row.  The child is invoked directly,\n    not resolved through '$PATH', and can not recurse (a child is called as 'bar'); when it lists\n    nothing -- no 'Bar.d' here and none installed -- the union is simply empty.  '--check' is not\n    affected: it validates the personality's own module directory (the site directory for 'makeitso').\n\n    Neither field may contain a '|' and a declaration is one line.  There is no index file: the\n    repertoire is *generated* by scanning the modules, so a new helper is one rule plus one\n    declaration and nothing else has to be kept in sync.  'bar helpers --check' (hooked into\n    'lints' of bar's own Barf) fails when a rule of a helper module is not declared, when a\n    declaration does not resolve to the rule below it, when a helper is declared twice, when its\n    name would autoload another module, when the module header names another file, when a\n    declaration sits in a file that is no helper module, when a declaration is separated from its\n    rule by other code or when it uses an unknown kind ('# helper-foo:' where one of the three\n    forms above was expected).\n\n    The index lists exactly the declarations the guard accepts and rejects nothing the index\n    shows: both are views of the same single validation pass, so a helper which is not listed is\n    always a reported violation and a listed helper always resolves.  A rejected declaration is\n    counted on stderr and named by 'bar helpers --check'.\n\n    OUTPUT CONVENTIONS (requirements, not style)\n\n      * print what the consumer needs for the next step: output << input;\n      * the shape is the helper's choice (lines, table, JSON) but stable and documented, counts\n        are an explicit line of their own and never implied;\n      * print less than everything only together with the exact elision counts ('n of N') and the\n        path of a file holding the complete, untruncated data, so nothing is lost;\n      * exit codes: 0 answered, non-zero could not answer, and a distinct code where the caller\n        has to tell 'no data' from 'error'; an empty result is an answer, not a failure;\n      * stdout carries only the result -- no progress chatter and no ANSI sequences -- stderr may\n        carry diagnostics, and a helper never pages interactively ('less' blocks an agent turn,\n        see Bar.d/search for that anti-pattern);\n      * read the data directly (file, CLI, sqlite, git) instead of going through an interface\n        which refuses to run outside its own context;\n      * '--json' stays opt-in: text for reading, one object for a wrapper.\n\n    A helper module needs no edit anywhere else: it becomes reachable for 'makeitso' with\n    'bar init_install <prefix>' (which links every module that is not linked yet into\n    '~/.config/makeitso', see Bar.d/init) and for a project with 'bar init --update' (which copies\n    new modules into the project's 'Bar.d/').\n\n    Functions:\n\n      helpers_module_of <rule>\n        the module 'rule_autoload' loads for <rule>\n        Mirrors 'rule_autoload' in Bar.d/std_lib: the prefixes are stripped, then everything from\n        the first underscore on.  'rad_state' -> 'rad', 'has_hermes_logs' -> 'hermes'.\n\n      helpers_personality - 'bar' or 'makeitso', the personality this invocation lists for\n        Bar has two personalities (see the module header and Bar.d/init): 'bar' is the project and\n        installation one, 'makeitso' the private one of the single site directory '~/.config/makeitso'.\n        The name bar was called by is in 'BAR_CALLED_AS' (set in the bar script from '$0'), anything\n        but 'makeitso' is treated as 'bar' so a direct 'bar', './bar' or an odd symlink still lists\n        the repertoire a project expects.\n\n      helpers_union_child [args..]\n        'bar helpers' in the cwd for the 'makeitso' union\n        'makeitso helpers' lists the repertoire of the place it is run in as well: 'bar helpers' in\n        the current working directory, which is the vendored or project repertoire there.  The child\n        is invoked directly through '$BAR_SELF' and never through a '$PATH' lookup, so a 'makeitso'\n        can not pick a foreign 'bar'; it is called as 'bar', so it can not recurse into a union of\n        its own.  A 'bar' which can not list a repertoire here -- no 'Bar.d' in sight and none\n        installed -- contributes nothing and its diagnostics are swallowed: an empty half is not an\n        error of 'makeitso helpers'.\n\n      helpers_union_rows - the 'bar' rows the 'makeitso' union appends, without their count\n\n      helpers_records - scan '$BAR_DIR' for the repertoire, as US (0x1f) separated records\n        One awk pass over the module files of '$BAR_DIR', nothing is loaded and no helper is run.\n        The records feed the single validation pass below:\n          <file> <line> M <word>                                       a '# helper-module:' header\n          <file> <line> D <name> <purpose> <usage> <output> <n> <word> <kind> a declaration\n          <file> <line> U <kind>                                       an unknown '# helper-<kind>:' line\n          <file> <line> R <rule>                                       a rule of a helper module\n          <file> <line> S                                              the code line separating a\n                                                                       declaration from its rule\n        'D' and 'U' records are emitted for every file -- a declaration outside a helper module is a\n        violation and has to be seen, and so is an unknown kind -- while 'R' and 'S' are only emitted\n        for a file which names a helper module, so a module without helpers costs nothing.  'S' is\n        the one code line that separates a declaration from its rule; it is decided here and not by\n        the walk, so the walk costs one iteration per declaration and rule instead of one per line of\n        a helper module.  The kind of a 'D' record is 'generic' for a plain '# helper:', 'bar' for\n        '# helper-bar:' and 'makeitso' for '# helper-makeitso:' and decides which personality lists it.\n        A file which neither names a helper module nor declares a helper contributes no record at all\n        and is dropped before the walk, with one 'grep -l' that stops at the first matching line: the\n        index stays cheap while the module sources grow (a byte of a module nobody declares a helper\n        in costs a 'grep' byte, not an 'awk' line), and the records are the ones the walk would have\n        produced from the whole directory.\n        Fields are separated by US (0x1f) and not by a tab: a declaration may have empty fields\n        (that is one of the violations) and a tab is IFS whitespace, which 'read' would collapse.\n\n      helpers_declaration_error <name> <purpose> <usage> <output> <fields> <word>\n        why is a declaration unusable?\n        Prints the reason, and nothing when the declaration is usable.  One implementation for the\n        whole module.\n\n      helpers_walk - the one validation pass behind the index and the guard\n        Walks the records of 'helpers_records' and prints, in file and line order:\n          !<file>:<line>: <problem>                        every violation\n          +<name> <purpose> <usage> <output> <word> <file>  a declaration which resolves\n        A declaration is only accepted once the rule directly below it has been seen, so this\n        single pass covers registration, resolution, adjacency and naming at once.  The index shows\n        the '+<...>' lines and the guard reports the '!<...>' lines, which is what keeps the two\n        from drifting apart.  A declaration is listed only when its kind addresses the personality\n        of this invocation ('generic' always, 'bar' only for 'bar', 'makeitso' only for 'makeitso'),\n        while every violation is reported no matter which kind it belongs to.\n        leaving a file: an open declaration and a module without helpers are violations\n        there.  The sentinel record below leaves the last file through this same branch.\n\n      helpers_declarations - the registered helpers as '<name> <purpose> <usage> <output> <word> <file>'\n        Sorted by name; the file is the module file as it was found in '$BAR_DIR'.  The violations\n        'helpers_walk' reports are counted on stderr, the guard names each one.\n\n      helpers_index_text - the text index: one row per helper, then the count line\n        The rows of this personality, then ('makeitso' only) the rows of the union, then a split line\n        and the count line.  The invocation of every row is prefixed with the personality it belongs\n        to, and the count is the number of listed rows -- the same total the JSON form reports.\n\n      helpers_index_json - the JSON index: one object per helper, count and both halves of a union\n        One object: 'bar_helpers':1, the count and one object per listed helper.  Every object\n        carries the personality in 'command' and has it prefixed on 'invocation' (a wrapper can tell\n        a 'makeitso' helper from a 'bar' one).  For 'makeitso' the 'bar' repertoire of the current\n        directory is merged in as well -- summed count, both halves' objects, again without dedup.\n\n      helpers_check_body - the registration guard for every helper module\n        Prints every violation 'helpers_walk' reports, one line each, and returns 1; prints nothing\n        and returns 0 when the repertoire is complete.\n\n      helpers [--json|--check]\n        the repertoire, see the module header\n        Body of the 'helpers' rule: the index (text or JSON) or the registration guard.  The\n        machine form is also entered with 'bar --json helpers' (BAR_AGENT_JSON, see\n        Bar.d/agent_rules).  An unknown argument is a usage error, exit 2.\n\n    Rules:\n\n      helpers\n        The repertoire index: one line per registered helper ('<name>  <purpose>  | <invocation>  |\n        <output shape>') sorted by name, then the total count as the last line.  '--json' prints one\n        object with the same count, '--check' verifies the registration of every helper module.\n\n      helpers_check\n        Verify that every helper module registers its rules ('bar helpers --check'): the guard is a\n        check target, so 'bar check helpers_check' reports it with the agent contract (verdict, chain,\n        totals, log path) and 'rule lints: helpers_check' can hook it into the lint target.\n\n  hermes - Retrieval over the Hermes log corpus -- what the logs of this host say about <X>, in one call\n\n    Hermes writes a corpus of text on this host: the agent and gateway logs, one worker transcript\n    per kanban task, one output document per cron run, and the session store.  The corpus is large\n    (megabytes, millions of lines) and reading it with ad hoc pipelines -- 'grep' here, 'sed' there,\n    a 'sqlite3' query next -- costs several turns and floods an agent's context with raw\n    transcripts.  'hermes_logs' is the one deterministic answer instead: it streams the corpus once,\n    prints a bounded digest of the matching lines and keeps the complete match list on disk.\n\n      makeitso hermes_logs --grep <ere> --since 1d      # a bounded digest and the exact total\n      makeitso hermes_logs --grep <ere> --count         # only the number\n      makeitso hermes_logs --grep <ere> --json | jq .   # one object for a wrapper\n\n    THE CORPUS (explicit, never guessed)\n\n    One *corpus root* is a Hermes home directory; the default is '$HERMES_HOME' and falls back to\n    '~/.hermes'.  A root contributes the sources below, and only these -- nothing is searched\n    implicitly, no directory is discovered by walking:\n\n      logs       <root>/logs/*.log, *.log.gz             the agent/gateway/gui logs\n      workers    <root>/kanban/logs/*.log, *.log.gz      one worker transcript per kanban task\n      cron       any file below <root>/cron/output/      'cron/output/<job>/<stamp>.md' and the\n                                                          flat legacy 'cron/output/<job>_*.txt'\n      sessions   <root>/sessions/*.jsonl, *.jsonl.gz     diverted (transcript) session records\n      store      <root>/state.db                         the SQLite session store, read only\n\n    The root is overridable with '--root <dir>' (repeatable) or with the environment variable\n    BAR_HERMES_LOGS_ROOT (':' separated); '--root' wins.  Other Hermes profiles are addressed with\n    '--root ~/.hermes/profiles/<name>', never by guessing: a path that differs between the gateway,\n    the TUI and a worker is exactly what an agent must not hardcode.  With more than one root every\n    locator carries the index of its root ('#2/logs/agent.log:88') and one 'corpus:' line names each.\n\n    THE ANSWER (one record per matching line)\n\n      <locator> <TAB> <timestamp> <TAB> <snippet>\n\n      locator       '<source path>:<line>' relative to its root ('logs/agent.log:1234'), so\n                    'sed -n 1234p <root>/logs/agent.log' retrieves the line byte for byte.  A store\n                    record has no file line and reads 'state.db:<session>/<message>/<line>', which\n                    'sqlite3 -readonly <root>/state.db' resolves with 'where id=<message>'.\n      timestamp     'YYYY-MM-DD HH:MM:SS' local time -- the stamp on the line when it has one (at the\n                    line start for a log line, anywhere on a JSON record), otherwise the stamp of the\n                    newest line above it that had one, otherwise the file's modification time.  A\n                    leading '~' marks those two derived cases, an exact stamp carries no marker.  A\n                    store record carries the message's own timestamp.\n      snippet       the line with its control characters removed, clipped at\n                    BAR_HERMES_LOGS_SNIPPET characters (default 160) with the number of characters\n                    left out appended as '<+N>'.  Tabs separate the fields; a snippet never holds one.\n\n    Then the totals, as lines of their own and never implied:\n\n      corpus: <root> [<sources scanned>]   matches: <n>   shown: <s> of <n> (limit <l> lines, <c> chars)\n      clipped: <k> snippet(s) at <c> chars   unreadable: <u> source(s) not scanned: <name>..\n      max: <path>\n\n    'matches' is the exact number of matching lines, a hard assertion: it is the count the scan\n    produced over exactly the sources the 'corpus:' lines name, and it is qualified by 'unreadable'\n    when a source could not be read.  The digest prints at most '--limit' match lines (default 20,\n    BAR_HERMES_LOGS_LIMIT) and at most BAR_HERMES_LOGS_BYTES characters of them (default 1200), so\n    the answer stays around a kilobyte however busy the corpus is.  Whenever anything stays unshown\n    the complete match list is on disk before anything is printed and its absolute path is printed as\n    'max:'.  That file holds every match ('<locator> <TAB> <timestamp> <TAB> the whole line'), up to\n    BAR_HERMES_LOGS_MAX records (default 20000) -- beyond that the file is capped and 'max:' says so.\n    It is written below BAR_HERMES_LOGS_DIR (default $TMPDIR) and named after the query, so the same\n    question reuses one file and the answer stays retrievable.\n\n    FILTERS\n\n      --grep <ere>     an extended regular expression, matched against every line of every scanned\n                       source with 'grep -E' semantics.  Without it every line matches (the digest is\n                       still bounded, the count still exact).  It is the only thing that decides what\n                       counts: no keyword prefilter, no sampling, no ranking.\n      --since <when>   '<n>[smhdw]' (ago) or anything GNU 'date -d' accepts ('2026-10-10',\n                       '2026-10-10 08:00:00'); records before the cutoff are dropped.  In the store\n                       the range is pushed into SQL, which makes a bounded session query cheap.\n      --source <kind>  restrict to a source kind, repeatable.\n      --task <sel>     keep only the sources whose path contains <sel>: '--task t_deadbeef' keeps a\n                       worker transcript, '--run 2026-10-10' the cron run of that day.  Applied to\n                       the source path, so the store never matches a task selector.\n      --session <id>   keep only the records of that session: store rows whose session id contains it\n                       (in SQL), 'sessions/<id>.jsonl' files by name, and the lines of every other\n                       source by their text (the agent logs carry '[<session-id>]').\n      --count          the number alone -- it wins over '--json', because one number is already the\n                       machine form.  Nothing is elided here, so no 'max:' line is printed.\n\n    EXIT CODES: 0 answered, matches found.  3 answered, no match ('grep' semantics: an answer, not a\n    failure -- the count is printed).  2 usage error.  1 could not answer: a root is not a directory,\n    or a source could not be read -- the digest is still printed, marked with 'unreadable:' and with\n    the reason on stderr, and the count then covers only what was readable.\n\n    NON GOALS: no summarisation, no judgement, no LLM, no ranking -- it is a filter.  It does not\n    replace the 'session_search' tool for fuzzy recall, and it does not read what Hermes stores\n    outside the paths above (the kanban board as a database, cron job definitions, the vault).  It\n    never pages, never colours, and prints no progress on stdout.\n\n    HANDOFF BRIEFS ('hermes_handoff')\n\n    A child session ('delegate_task') starts with no memory of the conversation that spawned it, so\n    the caller hands it a brief.  'hermes_handoff' composes that brief, resolves its anchor phrases\n    against the session store and checks that a child could retrieve the session -- one rule, three\n    modes:\n\n      hermes_handoff --goal <line> [--context <text> | --context-file <path>]\n                     [--anchor <phrase>].. [--gate <line>].. [--report-chars <n>]\n                     [--session <id>] [--db <path>] [--json]\n          a brief in seven sections, in order, one heading each, plain text: GOAL, CONTEXT,\n          RETRIEVAL, ANCHORS, MECHANICS, ACCEPTANCE GATES and REPORT FORMAT.  GOAL, CONTEXT and\n          every --gate line are printed verbatim; each --anchor phrase resolves to its messages.\n      hermes_handoff --anchors <phrase>.. [--limit <n>] [--session <id>] [--db <path>] [--json]\n          one 'msg <id> <role> <snippet>' line per distinct matching message, then 'matches: <n>'.\n      hermes_handoff --verify [--session <id>] [--db <path>] [--json]\n          whether a child could retrieve this session: 'session <id> msgs=<n> chars=<n>\n          retrieval=ok|missing', then the two retrieval recipes and the live delegation caps.\n\n    THE SESSION STORE\n\n    The store is a read only SQLite database, '${HERMES_HOME:-$HOME/.hermes}/state.db', overridden by\n    '--db <path>' or '$HANDOFF_DB'.  The session id comes from '--session <id>' or\n    '$HERMES_SESSION_ID'; the profile -- the first segment of an '@session:<profile>/<id>' link --\n    from '$HERMES_SESSION_PROFILE', default 'default'.  'messages' and 'sessions' are read with the\n    'sqlite3' CLI: no LLM and no summariser.  An anchor phrase resolves to the messages whose\n    'content' contains it, case insensitively; a phrase that resolves to nothing is reported as\n    'UNRESOLVED: <phrase>' and never guessed.  Nothing host local is hardcoded: the caps are read\n    live with 'hermes config get delegation.<key>' and '$HERMES_MAX_ITERATIONS'.\n\n    EXIT CODES\n\n      0 answered; 1 --anchors matched nothing; 2 usage error; 3 the session or the store is unknown\n      or unreadable; 4 the brief has an unresolved anchor (the whole brief is still printed).\n\n    The text and the '--json' forms carry the same numbers; '--json' is one object.\n\n\n    Variables:\n\n      BAR_HERMES_LOGS_ROOT (gx)\n        ':' separated corpus roots, '--root' wins\n        Default: ${BAR_HERMES_LOGS_ROOT:-}\n\n      BAR_HERMES_LOGS_LIMIT (gx)\n        match lines the digest prints\n        Default: ${BAR_HERMES_LOGS_LIMIT:-20}\n\n      BAR_HERMES_LOGS_SNIPPET (gx)\n        characters kept of a matching line\n        Default: ${BAR_HERMES_LOGS_SNIPPET:-160}\n\n      BAR_HERMES_LOGS_MAX (gx)\n        records kept in the full match list\n        Default: ${BAR_HERMES_LOGS_MAX:-20000}\n\n      BAR_HERMES_LOGS_BYTES (gx)\n        characters of match records the digest prints\n        Default: ${BAR_HERMES_LOGS_BYTES:-1200}\n\n      BAR_HERMES_LOGS_DIR (gx)\n        where the full match list is written\n        Default: ${BAR_HERMES_LOGS_DIR:-}\n\n    Functions:\n\n      hermes_logs_selector <option> <value>\n        reject a selector neither SQL nor the shell can carry\n\n      hermes_logs_since <when>\n        resolve '--since' into HERMES_LOGS_Q_CUTOFF and Q_EPOCH\n        The cutoff is the local 'YYYY-MM-DD HH:MM:SS' the line timestamps are compared against (for\n        that shape a lexicographic comparison is a chronological one), the epoch is the same instant\n        for the SQL range.  Both are set here, or the call fails.\n\n      hermes_logs_wanted <kind> [kind..]\n        is this source kind wanted? (no kind: all)\n\n      hermes_logs_emit_files <root> <index> <kind> [file..]\n        source records, sorted by path\n        One record per file: '<kind>\\t<index>\\t<relpath>\\t<mtime>', the mtime in the shape of a line\n        timestamp, so a source without timestamps is still dated.\n\n      hermes_logs_files <root> <index> [kind..]\n        the corpus of one root as source records\n\n      hermes_logs_program - the awk filter, one program for a text source and for the store\n        It reads its query from the environment ('HL_*') and not from 'awk -v': '-v' would interpret\n        backslash escapes in the pattern and silently search for a different expression.  It streams:\n        one line in, at most two records out (the full match list and the digest), never a buffer of\n        matches.  A store row is '<session> 0x1f <message> 0x1f <timestamp> 0x1f <content>', the\n        content's newlines encoded as 0x01.\n\n      hermes_logs_scan_file <file> <locator> <mtime> <session-filter>\n        scan one text source\n\n      hermes_logs_scan_store <db> <locator> <mtime>\n        scan the session store, read only\n        The store is read with 'sqlite3 -readonly': it never creates a journal and never touches the\n        live writer's WAL.  '--since' is pushed into SQL and the session selector becomes\n        'instr(session_id, ..)' so that it stays a substring match and no LIKE wildcard changes its\n        meaning.  The content is deliberately *not* prefetched by keyword: the pattern decides what\n        counts, in awk, exactly as it does for a file.  A newline inside a message is encoded as 0x01\n        and a stray 0x1f as 0x1e, so one row is one input line; both are control characters and are\n        removed from the snippet anyway.\n\n      hermes_logs_used <kind> <root-index>\n        remember which sources one root contributed\n        The digest: the bounded match lines, then the totals as lines of their own.\n        The same numbers and the same records as one object, for a wrapper.\n\n      hermes_logs_scan - scan the corpus of HERMES_LOGS_Q_ROOTS and print the answer\n        Streams every source once, appends every match to the full match list (bounded by\n        HERMES_LOGS_Q_MAX), keeps the digest records (bounded by HERMES_LOGS_Q_LIMIT) and prints one\n        of the three answers.  Returns the exit code of the module's contract.\n\n      hermes_logs [--grep <ere>] [-i|--ignore-case] [--since <when>] [--source <kind>].. [--task <sel>] [--session <id>] [--limit <n>] [--count] [--json] [--root <dir>]..\n        the match, range and count questions over the Hermes log corpus\n\n      hermes_handoff_home - the Hermes home directory\n        '$HERMES_HOME' when set, '~/.hermes' otherwise.  Resolved here and nowhere else so no path of\n        one host leaks into the rule.\n\n      hermes_handoff_db <override>\n        the session store path\n        '--db' wins, then '$HANDOFF_DB', then the store below the Hermes home.\n\n      hermes_handoff_profile - the profile of an '@session:' link\n\n      hermes_handoff_session <override>\n        the session id, or empty\n\n      hermes_handoff_trim <text>\n        <text> without leading or trailing whitespace\n\n      hermes_handoff_cap_text <value>\n        <value>, or 'unavailable' when it is empty\n\n      hermes_handoff_sql <db> <sql>\n        run one read only query, US separated fields\n        'sqlite3 -readonly' never creates a journal and never writes to the store.\n\n      hermes_handoff_quote <value>\n        a SQL single quoted literal\n\n      hermes_handoff_match <phrase>\n        the SQL predicate matching one anchor phrase\n        A case insensitive substring match over the message content.  'instr' -- not 'like' -- is\n        used so no wildcard of the phrase has a meaning of its own.\n\n      hermes_handoff_store_ok <db>\n        0 when <db> is a readable session store\n\n      hermes_handoff_session_exists <db> <session>\n        0 when the session is in the store\n\n      hermes_handoff_resolve <db> <session> <phrase> [<limit>]\n        the matching messages\n\n      hermes_handoff_count <db> <session> <phrase>\n        the number of matching messages\n\n      hermes_handoff_stats <db> <session>\n        the message count and total content size\n        Sets HERMES_HANDOFF_MSGS and HERMES_HANDOFF_CHARS.\n\n      hermes_handoff_record <record>\n        'msg <id> <role> <snippet>' from one US record\n\n      hermes_handoff_config <key>\n        one 'hermes config get delegation.<key>' value, or empty\n\n      hermes_handoff_caps - read the live delegation caps into the HERMES_HANDOFF_CAP_* variables\n        Read live and never hardcoded: the spawn depth and the summary budget come from the Hermes\n        configuration, the child iteration cap from '$HERMES_MAX_ITERATIONS' when set and the\n        configuration otherwise.\n\n      hermes_handoff_caps_line - one 'caps:' line\n\n      hermes_handoff_recipes <db> <session>\n        the two exact retrieval recipes\n\n      hermes_handoff_json_strings <text..>\n        the texts as a JSON array of strings\n\n      hermes_handoff_json_records <record..>\n        the US records as a JSON array of objects\n\n      hermes_handoff_brief_run <session> <db> <json> <goal> <context> <report_chars>\n\n      hermes_handoff_anchors_json <session> <db> <limit> <matched> <printed> <full>\n\n      hermes_handoff_anchors_run <session> <db> <json> <limit>\n        Whatever the digest leaves out stays retrievable from a file whose path is printed next\n        to the elision counts.  Written through a temporary name.\n\n      hermes_handoff_verify_json <session> <db> <retrieval>\n\n      hermes_handoff_verify_run <session> <db> <json>\n\n      hermes_handoff --goal <line> [--context <text>|--context-file <path>] [--anchor <phrase>].. [--gate <line>].. [--report-chars <n>] | --anchors <phrase>.. [--limit <n>] | --verify [--session <id>] [--db <path>] [--json]\n        the brief of a delegated child session, its resolved anchors, or whether a child could retrieve the session\n\n    Rules:\n\n      hermes_logs\n        The one deterministic answer to 'what do the logs of this host say about <X>'.  Streams the\n        corpus once (agent/gateway logs, kanban worker transcripts, cron run outputs, the session store),\n        prints a bounded digest and the exact match count, and keeps the complete match list on disk.\n        Exit 0 answered, 3 for no match, 2 usage error, 1 a source could not be read.  See the module\n        header for the corpus, the locator format, the filters and the non goals.\n\n      hermes_handoff\n        One rule, three modes: a child session brief, the resolved anchor phrases, and whether a child\n        could retrieve the session.  Reads the Hermes session store with 'sqlite3' and the delegation\n        caps live -- no LLM.  Exit 0 answered, 1 --anchors matched nothing, 2 usage error, 3 the session\n        or the store is unknown/unreadable, 4 the brief has an unresolved anchor.  See the module header.\n\n  hermeskanban - The Hermes Kanban board state digest -- the board questions in one call\n\n    Hermes Kanban board state: what the local board holds right now, bounded.\n\n    'hermeskanban' answers the board questions that otherwise cost several turns of hand written\n    sqlite -- and that 'hermes kanban' refuses to answer at all inside a worker context -- by\n    reading the board database directly: the counts by status together with how stale the oldest\n    ready card is and how many runs are live (the summary, which lists no card); the cards matching\n    a filter, one per line, with their dependencies and their live run; why a blocked card is\n    blocked; and what a card's runs did.\n\n      hermeskanban                              the bounded summary digest, no card listed\n      hermeskanban --status blocked             the blocked cards, one line each, with the reason\n      hermeskanban --assignee default           the cards of one agent, highest priority first\n      hermeskanban --parent t_deadbeef          the children of one card\n      hermeskanban --runs t_deadbeef            the runs of one card, newest first\n      hermeskanban --json                       the same numbers as one JSON object\n\n    A listing is bounded by '--limit' (default 50) and prints the exact elision counts plus the path\n    of a file holding every matching card whenever it elides anything; the summary always fits in\n    six lines.  Nothing but the answer goes to stdout.\n\n    EXIT CODES: 0 answered (a filter matching no card is an answer, not a failure), 2 usage error,\n    3 the board could not be read (no database, not a Kanban database, 'sqlite3' missing), 4 the\n    card asked for ('--id', '--parent', '--runs') does not exist.\n\n    An option is nothing without its value: '--status' with nothing after it is a usage error like\n    any other, and so is an empty value ('--id \"\"'), which would otherwise name no card and quietly\n    answer as though the option had not been given at all.\n\n    The board is resolved the way the kernel resolves it: '--db', then '--board', else\n    '$HERMES_KANBAN_DB', '$HERMES_KANBAN_BOARD' or '<root>/kanban/current', finally the default\n    board.  '<root>' is '$HERMES_KANBAN_HOME' or '~/.hermes'; a named board lives in\n    '<root>/kanban/boards/<slug>/kanban.db'.  The first line of the summary names the board after\n    the database it read: 'default', the slug of a named board, or 'custom' for anything else.\n\n    Functions:\n\n      hermeskanban_root - '$HERMES_KANBAN_HOME' or '~/.hermes'\n\n      hermeskanban_db_default - the database the kernel would resolve for this caller\n\n      hermeskanban_db_path <db option> <board option>\n        the path the explicit options resolve to\n\n      hermeskanban_board_name <board option>\n        the board slug for the output header\n\n      hermeskanban_sql <sql>\n        run one query, print its rows with US separated fields\n        '-readonly' keeps a board the caller may not write untouched; the second attempt covers a\n        WAL database whose shared memory sidecar the kernel has not materialised yet.  A failure of\n        both is a failure to read the board, which the caller turns into exit code 3.\n\n      hermeskanban_quote <value>\n        a SQL single quoted literal\n\n      hermeskanban_in_list <value..>\n        a quoted, comma joined SQL literal list\n\n      hermeskanban_project_id <id-or-slug>\n        the project id, or the argument unchanged\n        '<root>/projects.db' maps a slug to its id; without it (or without that project) the\n        argument is taken as an id verbatim, so a board database alone is enough to filter.\n\n      hermeskanban_json <text>\n        sets HERMESKANBAN_JSON to <text> as a JSON string literal\n        Sets a global instead of printing: a command substitution would fork a subshell for every\n        field of every record, which is what makes a listing of a hundred cards take a second\n        (measured 800 ms for 90 cards before this, 90 ms after).  The escaping and the 'into a\n        variable' form are 'json_set_string'; what stays local is kanban's own policy -- every\n        control character becomes a space instead of being dropped, so a title keeps its word\n        boundaries ('title with\\na newline' reads 'title with a newline').\n\n      hermeskanban_json_number_or_null <number|''>\n        sets HERMESKANBAN_JSON to a JSON number, or 'null'\n        The argument shape is kanban's (a run id or a duration as sqlite3 prints it, '' for none),\n        the literal is 'json_set_number'.\n\n      hermeskanban_duration <seconds>\n        '3d4h', '5h12m', '12m30s' or '45s'\n\n      hermeskanban_stamp <epoch>\n        a local 'YYYY-MM-DD HH:MM:SS', '-' for 0\n\n      hermeskanban_truncate <text> <max>\n        <text>, shortened to <max> bytes with a '...' tail\n\n      hermeskanban_card_sql <where clauses..>\n        the SELECT of one card record\n\n      hermeskanban_card_field <record> <index, 1 based>\n        one field of a card record\n\n      hermeskanban_card_reason <kind> <reason> <error>\n        why a blocked card is blocked\n\n      hermeskanban_card_line <record> <reason budget, 0 = full>\n        one card as one text line\n        The record is split once, here: a 'read' per field would fork a subshell per field and cost\n        a hundred milliseconds over a listing (measured on a 90 card answer).\n\n      hermeskanban_gather_cards <where clauses..>\n        load every matching card into HERMESKANBAN_ROWS\n\n      hermeskanban_card_exists <task-id>\n        0 when the card is on the board\n\n      hermeskanban_write_all <suffix> <budget>\n        write every record, print the path of the file\n        The complete, untruncated answer: the convention is that whatever the screen leaves out is\n        retrievable from a file whose path is printed next to the elision counts.  Written through a\n        temporary name so a reader never sees a half written file.\n\n      hermeskanban_record_line <record> <budget>\n        one record through the active renderer\n\n      hermeskanban_status_count <status>\n        the count of one status, 0 when it is not a status\n\n      hermeskanban_list_view <json> <limit>\n        the matching cards, oldest of one priority first\n\n      hermeskanban_list_json <printed> <limit>\n        the listing as one JSON object\n\n      hermeskanban_runs_sql <task-id>\n        the SELECT of one run record\n\n      hermeskanban_gather_runs <task-id>\n        load the runs of a card into HERMESKANBAN_ROWS\n\n      hermeskanban_run_line <record> <budget>\n        one run as one text line\n\n      hermeskanban_runs_view <task-id> <json> <limit>\n        the runs of one card, newest first\n\n      hermeskanban_runs_json <task-id> <status> <kind> <path> <branch> <title> <printed> <limit>\n\n      hermeskanban [--json] [--board <slug>|--db <path>] [--status <s>[,<s>]..] [--assignee <name>] [--project <id|slug>] [--parent <task-id>] [--id <task-id>] [--limit <n>|--all] [--runs <task-id>]\n        the local Hermes Kanban board: the summary, the cards a filter selects, or the runs of one card\n\n    Rules:\n\n      hermeskanban\n        The bounded board state digest.  See the module header for the exit codes, the board resolution\n        and the elision rules; nothing but the answer is written to stdout.\n\n  init - Module to install, initialize and update bar itself\n\n    Functions:\n\n      init_install_modules <origin Bar.d> <target Bar.d>\n        installs the modules every bar needs\n        Only the modules which are loaded on every startup: 'std_lib' and 'rule_lib' (both\n        required by bar itself) and the '*_rules'.  Everything else is vendored on demand when a\n        rule first names it, which is what 'init_deps' does right after the installation.\n\n      init_deps - completes a vendored 'Bar.d' with the modules the Barf really needs\n        'bar init' installs the mandatory modules only ('init_install_modules'), this walks the\n        rules of the local 'Barf' and vendors every module they (transitively) require from the\n        installation bar was run from.  Run it again after the Barf grew a dependency to pick up\n        the modules it now needs.\n\n      init_update_merge_barf\n        Since the Barf is the users configuration we can't just update it this 'a bit mouthful' rule will do a merge of the local './Barf' with the shipped 'Barf.default'.  This will leave conflicts which have to be manually resolved. This is only useful when the shipped when 'Barf' was not or only slightly edited.\n\n      init_install [prefix]\n        install links in [prefix] or '~/.local'.\n        Bar will usually exist somewhere in a users home directory where it got checked\n        out. Bar does rolling releases, it is continuously improved. To make this checked out\n        version available for use this 'init_install' will create symlinks at the given\n        [prefix] directory or in '~/.local' by default.\n        For 'makeitso' every module of 'Bar.d/' is symlinked into '~/.config/makeitso'.  Running\n        'init_install' again adds the symlinks of modules which are not linked yet and leaves the\n        existing ones untouched, so a module which was added to 'Bar.d/' later becomes reachable\n        without a manual symlink.\n\n    Rules:\n\n      init\n        Initialize bar in a workdir. This copies the main 'bar' script, a default 'Barf' and the modules\n        bar loads on every startup ('std_lib', 'rule_lib' and the '*_rules') to the current directory,\n        then 'init_deps' installs the modules the new 'Barf' needs.  What is copied comes from the\n        installation this bar was run from: '<prefix>/bin/bar' with '<prefix>/share/bar/' beside it, or\n        the source tree the running 'bar' lives in ('<checkout>/bar' with '<checkout>/Bar.d'), so a\n        checkout can initialize a project without being installed first.  With '--no-install-deps' that\n        last step is left out, so the 'Barf' can be customized first and 'bar init_deps' run afterwards\n        to vendor exactly what the edited 'Barf' names.  Every other module is vendored from the\n        installation the first time a rule names it, so a 'Bar.d/' which was trimmed on purpose keeps\n        working.  When the workdir is a git repository then '.gitignore' is updated to exclude the files\n        and directories 'bar' will use for temporary/testing storage.  A user may edit the 'Barf' file to\n        suit the project/personal preference, run 'init_deps' after that and delete modules from 'Bar.d/'\n        which are not needed ('bar modules_prune' deletes the ones which are still unmodified copies of\n        the installation's).  When that is done the new/changed files should be committed to version\n        control.\n\n      init_deps\n        Install the modules the current 'Barf' needs into the vendored 'Bar.d'.  Rules which name a\n        module (as a dependency or in a 'require' line of their body) get it vendored from the\n        installation bar was run from, transitively.  Modules which are already there are left\n        untouched, this only ever adds.\n\n      init_update_existing\n        Updates a initialized working tree. That is 'bar' itself will be updated when a newer\n        version is available and any module in 'Bar.d/' that exists in the local working tree will\n        be updated as well.  Barf will be left untouched.\n\n      init_update\n        Updates a initialized working tree. That is 'bar' itself will be updated when a newer\n        version is available and all modules including ones that are not present in 'Bar.d/' will\n        be updated as well.  Barf will be left untouched.\n\n  json_lib - The JSON literals of bar's machine readable output: one escaping rule for every '--json' form.\n\n    The JSON forms of the helpers -- 'hermeskanban --json', 'rad_state --json', 'sourcetree_state\n    --json', 'hermes_logs --json', 'helpers --json', the report of 'bar check' -- are assembled from\n    string, number, boolean and array literals.  Writing one of them is one small piece of policy\n    and it belongs in one place:\n\n      json_string <text>             the JSON string literal of <text>\n      json_set_string <var> <text>   the same, left in <var> instead of printed\n      json_number <value>            <value> as a JSON number, 'null' when it is no number\n      json_set_number <var> <value>  the same, left in <var>\n      json_bool <text>               'true' for 'yes', 'false' for 'no', 'null' otherwise\n      json_list <text> [<sep>]       <text> cut at <sep> (default ','), as a JSON array of strings\n      json_array <literal..>         the literals as one JSON array\n\n    THE ESCAPING RULE\n\n    Backslash and double quote are escaped, a tab becomes the escape '\\t' and every other control\n    character is dropped; nothing else about <text> changes.  A literal therefore never carries a\n    raw control character, so one record is one line: an object can be held in a shell variable,\n    compared byte for byte and cut by line without a parser.  Dropping is deliberate -- a newline\n    inside a title would otherwise end the record.\n\n    'EMPTY IS NULL'\n\n    A field bar could not answer is 'null', not an empty string and not a zero: 'json_number'\n    answers 'null' for anything which is no number (an empty value included), and 'json_bool'\n    answers 'null' for a value which is neither 'yes' nor 'no', so an unset flag stays\n    distinguishable from a flag which is off.  An empty array is a different answer and stays '[]':\n    'no labels' is a fact.\n\n    WHY THE SETTERS EXIST\n\n    A command substitution forks a subshell, and a listing quotes a dozen fields per record:\n    'hermeskanban' measured 800 ms for 90 cards when every field forked and 90 ms when none did.\n    A caller which quotes many fields asks for the literal in a variable with the setters and never\n    forks; a caller with one or two fields asks for the printed form.  Both forms are one\n    implementation -- 'json_string' calls 'json_set_string' -- so the rule above cannot drift.\n\n    This module is a library: no rules, no helpers and therefore no helper module header of its own\n    (a lib is not a repertoire module, see Bar.d/helpers_rules).  A module whose '--json' form needs\n    literal says 'require json_lib'; where a field is not a JSON literal but module policy -- the\n    yes/no/unset rule of rad, the flag list of sourcetree, the control characters 'hermeskanban'\n    replaces by a space instead of dropping them -- that policy stays in the module, which asks\n    these functions for the literal and keeps only the decision.\n\n    Functions:\n\n      json_set_string <var> <text>\n        put the JSON string literal of <text> into <var>\n        The escaping rule of every '--json' form of bar.  Backslash and quote first (so the escapes\n        they introduce are not escaped again), then the tab -- which is the one control character\n        worth carrying, an indented line is common in a log -- and finally every other control\n        character, dropped with the same pattern substitution 'hermeskanban' uses: no 'tr', so no\n        subshell per field.\n\n      json_string <text>\n        a JSON string literal\n\n      json_set_number <var> <value>\n        put <value> as a JSON number into <var>, 'null' if none\n\n      json_number <value>\n        <value> as a JSON number, 'null' when there is none\n\n      json_bool <text>\n        'true', 'false' or 'null' for a yes/no/unset field\n\n      json_list <text> [<sep>]\n        the <sep> separated <text> as a JSON array of strings\n        The members are the fields of <text>, trimmed; an empty member is dropped, so an unset field\n        is '[]' and not '[\"\"]'.  The separator is '+' for the flag lists of a digest and ',' for\n        everything a user wrote.\n\n      json_array <literal..>\n        the literals as one JSON array\n\n  lock - Library for managing lockfiles\n\n    Functions:\n\n      lock_wait <lockname>\n        Waits until we have the lock on a lockfile.\n        '.lock' is automatically appended to the 'lockname'.\n        These locks are recursive the same process can lock_wait on the same lockfile multiple\n        times which must be paired with the same numbers of 'lock_remove' to unlock it.\n\n      lock_try_norec <lockname>\n        try to lock a lockfile non recursively.\n        Will not wait, fails when we already have the lock\n        Locks 'new' and then removes the 'old' locck.\n\n      lock_send <lock> <who>\n        Sends a 'lock' to another process with pid 'who'.\n        Locks can be send atomically to another process. 'lock_send' registers the lock to be\n        send to a process, the lock will be send at the final lock_remove in the sending\n        process when all recursive locks are freed. Note that to obtain the 'who' pid the\n        receiver has to be started first.\n\n      lock_receive <lock>\n        Wait for lock send by another process.\n        Blocks until the the sender removed all uses of 'lock',\n\n      lock_remove <lock>\n        Unlocks 'lock'.\n        'lock_remove' must be paired with the same numbers of earlier 'lock_wait'. Only the\n        last 'lock_remove' will release the lockfile.\n\n  maintenance_rules - A generic maintenance workflow where other modules can hook into\n\n    Rules:\n\n      maintenance\n        Doing maintenance work.\n\n      maintenance_prepare\n        Preparatory work before starting maintenance\n\n      maintenance_sync\n        Synchronize the worktree with other (upstream) sources\n\n      maintenance_toolchain\n        Toolchain related work, compatibility checks, necessary updates\n\n      maintenance_dependencies\n        Updates dependencies etc.\n\n      maintenance_security\n        Security/Audit checks\n\n      maintenance_configuration\n        Configuration and manifest maintenance/updates\n\n      maintenance_code\n        Code compatibility work, including tests when required\n\n      maintenance_documentation\n        Documentation work\n\n      maintenance_validate\n        Validation that the maintenance pass succeeded\n\n      maintenance_post\n        Post maintenance work\n\n      maintenance_cleanup\n        Maintenance cleanup\n\n  memodb - A persistent database which memoizes command results\n\n    It records the stdout, stderr and state/exitcode commands and functions.  Commands and\n    functions that are used with the memodb must be pure as their result must only depend on\n    the arguments passed and the directory they are called in. The content of the directory is\n    hashed for the database. This requires properly set up '.gitignore' rules to ignore any\n    build artifacts. Otherwise subsequent instances will not use the same memodb and re-run\n    commands.\n\n    This is used for\n     1. Persist/cache command results throughout multiple invocations\n     2. Implement background processing where one invocation can schedule commands to be\n        executed in the background and a later invocation can collect the results\n\n    Variables:\n\n      MEMODB_KEEP (gx)\n        How many trees to keep.\n        Default: ${MEMODB_KEEP:-5}\n\n      MEMODB_BACKGROUNDING (gx)\n        Global backgrounding flag.\n        Default: ${MEMODB_BACKGROUNDING:-true}\n\n    Functions:\n\n      memodb_eval <cmd> [args..]\n        Execute a command in foreground and stores its results.\n        The command is only executed on the first time the 'memodb_eval' is called with the\n        same options in the same directory. Any subsequent call will replay the stored results.\n\n      memodb_schedule <cmd> [args..]\n        Schedules a command to be run in background.\n        Will 'memodb_eval' the command when 'MEMODB_BACKGROUNDING' is not true.\n\n      memodb_result <cmd> [args..]\n        Retrieves the background results.\n        Waits for the backgrounding to be completed. Merges the results into the current memodb.\n        Replays the result (stdout/stderr/exitcode) of the given 'cmd [args..]'.\n        'cmd [args..]' must be the same as used when scheduling it to the background.\n        Will error when the command was not previously scheduled and backgrounding is enabled.\n\n  modules - Module to prune the unmodified copies out of a vendored 'Bar.d'\n\n    'bar init' and 'require' vendor the modules a project uses from the installation bar was run\n    from, so a project's 'Bar.d' grows with it and a module stays behind when the rules which named\n    it are dropped from the 'Barf'.  'modules_prune' removes those copies and completes the tree\n    again with 'init_deps'.\n\n    The name is what the module autoloader resolves the command to (a rule loads the module named by\n    the word before its first '_'), so this lives here and not in the 'init' module which vendors\n    and completes the tree.\n\n    Functions:\n\n      modules_standard <module>\n        true for the modules a vendored 'Bar.d' always carries\n        The modules 'init_install_modules' installs unconditionally, because they are loaded on\n        every startup: 'std_lib' and 'rule_lib' (both required by bar itself) and the '*_rules'.\n        'modules_prune' never removes them, everything else is vendored on demand.\n\n      modules_prune [--dry-run]\n        removes the unmodified vendored modules, then completes the tree again\n        'bar init' vendors the modules a project needs from the installation bar was run from\n        (:func:`init_deps`) and 'require' vendors the ones it starts to use later (see\n        :func:`bar_vendor_modules` in the bar script), so a project's 'Bar.d' grows with it.  A\n        module which is not needed anymore -- the rules which named it were dropped from the 'Barf'\n        -- stays behind.  This removes every module which is a byte identical copy of the one in the\n        origin and runs :func:`init_deps` afterwards, which vendors back the modules the current\n        'Barf' really needs.  What stays is the modules the project uses plus the ones it customized.\n        A module is only ever removed when the origin has one of the same name and the two are\n        identical.  A customized module (the content to preserve), a module the origin does not have\n        at all (it was written here) and one which is not a regular file (a link into the origin)\n        are left alone, and so are the standard modules (:func:`modules_standard`).\n        With [--dry-run] the modules which would be removed are reported and nothing is written,\n        'init_deps' does not run either.  Returns non-zero when there is no origin to compare with,\n        that is when the 'Bar.d' in use is the installation's own.\n\n    Rules:\n\n      modules_prune\n        Removes every module of a vendored 'Bar.d' which is an unmodified copy of the installation's one\n        and then completes the tree again with 'init_deps': what only the dropped rules needed\n        disappears, a customized module and one the origin does not have are preserved.  Modules are\n        compared byte for byte, only the installation's own modules can be removed, never anything the\n        project wrote.  With '--dry-run' nothing is removed and 'init_deps' does not run.\n\n  net - Low level networking functions\n\n    Functions:\n\n      net_canon_hostname <host>\n        Canonalize 'host' to a FQDN.\n\n      net_host_reachable <host> [timeout [tries]]\n        checks if 'host' is reachable.\n        The check uses ICMP ping to check for reachability.\n        The [timeout] defaults to 10 seconds and [tries] defaults to 3.\n\n      net_wakeonlan <host>\n        Sends a wake-on-lan signal.\n        'host' is a hostname. '/etc/ethers' must have entries to resolve hosts. See\n        'contrib/update-ethers.sh' for automatically discover and maintain '/etc/ethers'. Waits\n        for the host to become reachable. Fails when it is not reachable.\n\n  rad - The radicle domain of bar: what this checkout is, what it carries, whether the author of an\n    incoming item may be trusted, how a topic starts -- and how a repository gets onto radicle in\n    the first place.\n\n    The helpers, all served by the backing library Bar.d/radicle_lib (running 'rad', reading its\n    padded tables back, the age of a last event, the node storage, the '.worktrees/' exclusion and\n    the JSON literals):\n\n      bar rad_state [--json] [--max <n>]                     the whole state, one screen\n      bar rad_patches [--state <s>] [--author <a>] [--json] [--max <n>]\n                                                             only the patches, filtered\n      bar rad_trust --repo <rid> --author <who> [--grounds <..>] [--dump] [--second-party] [--requester <who>]\n                                                             which grounds of trust that author has\n      bar rad_comment_metadata <item> --query <label|assign|unassign>\n                                                             the trusted '*label:'/'*assign:'/\n                                                             '*unassign:' proposals of one item\n      bar rad_topic <class> \"<description>\"                  open the topic tree of an item\n      bar rad_reaction [--from <emoji>] <to> <target>..      set/transition/withdraw our own marker\n      bar rad_reaction --state [--json] <target>..           the live markers of a target, and the\n                                                             op that set each\n      bar rad_identity --repo <rid> [--did <who>] [--commit <sha>]\n                                                             the delegated identities, the git\n                                                             authors each one uses, and a commit\n                                                             tied to one\n      bar rad_cob <list|ops|document|state> ..              the COB store: what it holds, one chain,\n                                                             one document, and the state of every\n                                                             chain\n      bar rad_rm_label <item> <glob>..                       remove the stored labels a glob matches\n      bar rad_import                                         import this repository into radicle\n      bar rad_merge_accepted_prs [--dry-run] [--no-fetch] [--max <n>]\n                                                             merge the patches a delegate accepted\n      bar rad_rebase_ask [--after <patch>] [--branch <branch>] [--onto <sha>] [--dry-run]\n                                                             ask the pending patches of a branch to\n                                                             re-cut themselves after a merge landed\n      bar rad_branch_sync [--branch <branch>] [--dry-run] [--json]\n                                                             fast-forward a branch to its remote tip,\n                                                             or write nothing\n\n    Every helper answers '-h' or '--help' with the builtin help of its rule on stdout and exit 0\n    ('bar usage <name>'), and a refused argument with what was wrong on stderr and exit 2.\n\n    'rad_state' answers the questions an agent asks before it touches a radicle repository: which\n    repository is this (RID, name, path), which identity does 'rad' use here, how the patches stand\n    (how many, by state, ours against others), how the issues stand (state, labels, when something\n    happened last) and which local branches still carry commits no patch exposes.  'rad_patches' is\n    the patch section of that report, filtered by state and by author.\n\n    Nothing there goes to the network: 'rad patch/issue list' answer from the local node storage and\n    the divergence is measured against 'rad/main' as the last 'git fetch rad' left it, which the\n    'head' line names.  Syncing is the caller's business ('rad sync', 'git fetch rad'): this helper\n    reports what is there and says how old it is.  'rad_topic' and 'rad_branch_sync' are the two\n    exceptions: 'rad_topic' fetches the radicle remote (else 'origin') to *know* how the base relates\n    to upstream, 'rad_branch_sync' fetches it to move the base itself forward -- see below.\n    'rad_merge_accepted_prs' is the other exception to the \"nothing there goes to the network\"\n    reading: it fetches 'rad' first, because the branch a patch has to be merged into has to be\n    the current one.  It is also the one helper which writes to a canonical branch -- the one of\n    the checkout it runs in -- which is why it refuses under an identity which is not a delegate\n    of that repository, and why '--dry-run' exists for every other identity.  The acceptance it\n    acts on is read off the patch's own chain, never off 'rad patch list': a Reviews column cannot\n    say which revision a verdict names, and an acceptance of an older revision is exactly what\n    must not be acted on.\n\n    SOURCES\n\n      rad inspect --rid         the repository identity, 'git rev-parse' the toplevel\n      rad self                  alias and DID of the identity 'rad' uses here\n      rad patch list --all      patch id, title, author and head (the state is not in the list)\n      rad issue list --all      issue id, title, author, labels and assignees\n      rad patch/issue list --authored/--draft/--merged/--archived/--closed/--solved\n                                rad prints one state per call, so the states are subtracted as\n                                sets: 'open' is what is in '--all' and in none of the others, and\n                                'ours' is what '--authored' (patches) or '(you)' (issues) says\n      the node storage          time and kind of the last event of every patch and issue, read\n                                from 'refs/namespaces/*/refs/cobs/xyz.radicle.<type>' below\n                                '$(rad path)/storage/<RID>' with 'git for-each-ref'\n      git for-each-ref/rev-list the current branch, the local branches and their distance from\n                                'refs/remotes/rad/main' or 'refs/remotes/rad/master'\n      git ls-remote/log/branch  'rad_topic': the trunk on both sides and the commits in between\n\n    OUTPUT\n\n      Text: one header line per section -- '<section>  <counts>' -- then one line per item, the\n      free text last so that nothing has to be padded.  Ages are short ('6m', '2h', '1y') and are\n      those of the last event.  '--json': one object marked with 'bar_rad': 1, the complete data,\n      never elided.\n      At most '--max' items per section (default 10, '--max 0' lists all).  A section which is\n      cut states exactly how much ('n of N elided'), and when anything was cut the complete text\n      is written to a file whose path the last line prints, so the elision loses nothing.\n\n      'ours' is what 'rad' itself calls ours, everything else is 'other' (whose author is then\n      named behind the title); a patch which rad lists in more than one state is named by the\n      first of 'draft', 'merged', 'archived'.\n\n    EXIT CODES\n\n      0  answered (an empty repository -- no patch, no issue -- is an answer, not a failure)\n      2  usage error\n      3  no data: not a git repository, or no radicle repository here\n      1  could not answer: 'rad' is missing or failed, or its output was not understood\n\n    'rad_trust' answers whether the author of an incoming issue or patch counts as a *trusted\n    source* for the triage policy (`incoming-issue-triage` §3) -- and, above that, **which grounds of\n    trust** that author has, so the caller decides per action instead of paying for one fixed rule.\n    The grounds are a disjoint list (any one of them suffices, an implied OR, no expressions):\n\n      delegate      the author is a delegate of this repository\n      owner         the author is the identity which established the repository\n      opened        the author opened the item under discussion\n      assigned      the author is an assignee of that item\n      contributed   the commits the identity authored as itself reach the threshold\n\n    'owner' and the two item grounds are facts about the repository's own copy and its item, so they\n    need '--issue <id>' or '--patch <id>'; without an item they are 'false', never an error.\n\n      bar rad_trust --repo rad:z3WhBdHt… --author cehteh\n      trusted author=cehteh delegate=yes authored=396 threshold=10 ground=delegate reason=delegate of this repository repo=rad:z3WhBdHt1VVNyeQ61zpY66pNzuSrP\n\n      bar rad_trust --repo rad:z3WhBdHt… --author cehteh --grounds delegate,assigned --issue d896735\n      bar rad_trust --repo rad:z3WhBdHt… --author cehteh --dump --issue d896735    every ground, no verdict\n      bar rad_trust --repo rad:z3WhBdHt… --author cehteh --grounds contributed --threshold 10%\n\n    '--grounds' names the grounds which suffice (default 'delegate,contributed', the one rule this\n    helper answered before), and the 'ground' it reports is the first of them which holds, in the order\n    they were listed.  '--dump' answers every ground and evaluates *nothing*: no verdict, no threshold,\n    exit 0.  That is the call a caller makes once per scope and then caches -- the grounds are values,\n    the decision is the caller's -- so a second call in the same scope is wasted work.\n\n    '--second-party' answers the one question an approval asks: is this actor somebody *other* than\n    the identity which asked for the decision -- the second party a consent needs.  '--requester'\n    names that identity -- the loop, the agent, whoever opened the gate -- as a unique id, a bare NID\n    ('z6M…') or a 'did:key:' one; an alias is refused, because two identities can answer to one and a\n    comparison against the wrong one is worse than no answer.  The flag answers 'false' when the actor\n    *is* the requester and 'true' otherwise, and the comparison needs no item.  Without '--requester'\n    the requester **is the actor**, so the answer is 'false': the identity which asked is never a\n    second party to its own question, and the item's opener is not consulted.  The output is the dump\n    above plus one field, 'second_party'.\n\n    '--threshold' decides 'contributed' alone: a bare number counts commits (default 10), a percent\n    ('10%', '0.5%') is the share of the commits every identity authored in this repository, which is\n    what lets one setting mean the same in a big and in a small one.  The numbers come from the fact\n    tool 'RAD_TRIAGE_FACTS' names (delegates from the identity document, the commits an identity\n    authored as its own NID from the COB commits, plus the code commits of the humans behind its\n    patches); the grounds about the repository's own copy -- owner, opened, assigned and the total the\n    share is measured against -- are read from the node storage, and only when a ground asks for them.\n    The '--json' form is the whole object the tool answered plus 'grounds', and in verdict mode\n    'verdict', 'ground' and 'reason' as well.\n\n      exit 0 trusted -- 1 untrusted -- 2 could not answer (unknown author, no data, usage)\n\n    'rad_comment_metadata' is the second half of the same policy, for the writes nobody but a\n    delegate may make.  Radicle authorizes an issue's `Label`, `Assign` and `Lifecycle` actions to\n    the repository delegates (and to the issue's author for editing and closing it) -- verified in\n    the implementation (`heartwood`, 'crates/radicle/src/cob/issue.rs', 'Issue::authorization'), so\n    an incoming contributor *cannot* label or assign anything: the store refuses it.  Instead the\n    proposal travels as a **structured comment**: the first lines of a comment may be\n\n      *label:improvement\n      *assign:cehteh\n      *unassign:ct_vibes\n\n    one per line, starting at the comment (the issue description counts -- rad calls it the root\n    comment of the thread).  This helper reads those blocks back from the node storage and returns\n    only the entries a trusted source wrote:\n\n      bar rad_comment_metadata <item> [--query <key>].. [--repo <rid|path>] [--trust <spec>].. [--last] [--json]\n\n    The form is '*<key>:<value>' and the vocabulary is open: a key is a word of letters, digits, '_'\n    and '-' which starts with a letter, and *any* key is read back -- what a key means is the\n    caller's business, and only the keys with a registered effect ('label', 'rmlabel', 'assign',\n    'unassign') are ever applied (see the write layer below).  The block ends at the first line which\n    is not a marker, so a marker quoted inside a reply is prose.  A **value runs to the end of the\n    line** and may carry spaces: what a value may hold belongs to the directive that owns the key, so\n    the reader hands the line out as it is and only the label effect normalises it (a label is a word\n    without whitespace -- radicle writes a space as '-', and so does 'rad_label_name').  '--query'\n    names the keys to answer for and repeats; with no '--query' at all the answer carries every key a\n    comment proposes, and every line names its own key.\n\n    '<item>' is the issue or patch the question is about, short or full -- one item only, not the\n    whole repository: the ref listing is a single call, the chains (the ops and their payloads) are\n    opened for that COB alone.  An id the node does not hold is 'no data' (3), an id an issue and a\n    patch both match is a usage error (2).\n\n    '--trust' takes 'did:<did|nid>' (that identity, named) and 'contributor(<n>)' (an identity with\n    at least <n> authored commits); with no '--trust' only the **delegates** are trusted, which is\n    the narrow default.  '--last' keeps the newest block *per key* -- the last record of the chain\n    order, not of the hash order: two markers written in the same second are ordered by where they\n    sit in the chain (parent before child), which is what makes 'the newest block' mean what it says.\n    A block which is the newest word on two keys answers for both, and a newer block nobody can be\n    attributed to is skipped, so an older trusted block still applies.\n    The trust facts come from the same tool 'rad_trust' uses ('tools/radicle_triage/triage-facts').\n    A delegate's maintenance command reads the trusted proposals back and applies them -- the labels\n    and assignments themselves still need that delegate's own key.\n\n    The write layer of the same policy.  One function per effect, each of them exactly one 'rad'\n    invocation, and none of them asks about trust: that is the reader's decision, which is why the\n    reader is the only way in.  These are not rules -- they are the API a rule (or the daemon the\n    project builds elsewhere) composes, which is why they are named like the plumbing they are:\n\n      bar rad_set_label <item> [<add>] [<delete>]..    one 'rad issue|patch label' write\n      bar rad_assign <item> <did|alias>                one 'rad issue|patch assign' add\n      bar rad_unassign <item> <did|alias>              one 'rad issue|patch assign' delete\n      bar rad_apply_from_comments <item>.. [--query <key>].. [--repo <rid|path>] [--trust <spec>]..\n                                 [--dry-run]           the trusted proposals of an item, applied\n\n    '<item>' is the form the reader answers with ('issue:<id>', 'patch:<id>'); a bare id is an issue.\n    The identity of an assignment is resolved before it is written -- a comment cannot be trusted to\n    spell a DID right -- by the same fact command the trust gate asks, and a value which resolves to\n    nothing is refused rather than written as it stands.  Every applier reports what it applied\n    ('applied label item=issue:4444444 add=idea delete=wip') and no applier writes into the thread.\n\n    'rad_apply_from_comments' reads the newest trusted block per key (that is what applies; a block a\n    later one superseded is history) and dispatches every entry to the applier of its key: 'label',\n    'rmlabel', 'assign' and 'unassign'.  Any other key is reported and never guessed at.  An entry\n    whose author cannot be attributed never reaches it -- the reader drops those, silently, like every\n    proposal which is not trusted.  A label the item already says is not written again ('skip when the\n    labels already say it'), and '--dry-run' prints what would be applied without writing anything.\n    Nothing is remembered between calls: what the item carries is read fresh, from 'rad issue|patch\n    list', every time.\n\n    The labels are *one* state, and the colon decides which of them the applier maintains by itself.\n    A label carrying a ':' is namespaced -- the 'pr:<id>'/'issue:<id>' link labels and every other\n    '<namespace>:<rest>' -- and it is therefore **protected**: a chain transition never deletes it,\n    and it leaves the item only when a '*rmlabel:' glob or a direct write names it.  The colon-free\n    labels are the *chain slot*, and exactly one of those is kept: the delete set of a transition is\n    the stored colon-free labels, and the new one goes in with the old one out in the same write.\n    (Before the colon decided this, *every* stored label the newest block did not name was deleted,\n    which is what ate the link labels.)  A stored label no proposal names is reported and left alone\n    -- deleting on the strength of an absent proposal would reverse a delegate's write, and a\n    delegate may label by hand with no comment at all.\n\n    '*rmlabel:<glob>' is the explicit removal, and it runs *before* the labels of the same block: the\n    glob is matched against the whole stored label, anchored at both ends ('pr:*' takes 'pr:<id>' and\n    never 'xpr:<id>', '*' takes everything), a glob which matches nothing writes nothing and says so\n    (exit 0 -- nothing to remove is an answer), and the removal has to come first so that a block\n    carrying both a '*rmlabel:' and a '*label:' in the same namespace ends with the new label in.\n\n    'rad_reaction' is the same idea for the reaction markers, and those are a different animal: a\n    reaction is *private per identity* and can never be delegated, so there is no trust question here\n    -- what the caller compares and swaps is its *own* state, and that state is read from 'rad cob\n    show --format json': the store's own reduction of the COB, whose per-type shapes are unified here\n    into one list of the live markers of ours on the target.  A transition writes the marker to set\n    beside the one it takes back (one op, two actions), which is the shape the reduction is read\n    against; a reader reducing the op chain itself cannot tell a marker from one its author took back\n    in the very op next to it, and reports a superseded marker as live for ever (bar issue 9a815de).\n\n    The state of one target is a *set*: rad keeps one entry per emoji, so the same marker cannot be\n    live twice, while two *different* markers of ours can be.  One call is still one marker's business:\n    '<from>' names exactly one, the comparison asks only whether that one *is* live (never that it is\n    the whole state), and the step takes it back and sets '<to>'.  A second marker standing there is\n    left where it is, and a '<to>' which is already live is a no-op add.  'active' is a value and never\n    a toggle: a marker is taken back by its own action, not by writing it a second time.  The read is\n    the *current* state only -- 'rad cob show'\n    takes no '--from'/'--until' -- which is what a caller needs before it writes; the state at some\n    point in time is 'rad cob log --from/--until' instead.\n\n      bar rad_reaction [--from <emoji>] [--repo <rid|path>] <to> <target>..\n      bar rad_reaction <from> <to> <target>.. [--repo <rid|path>]\n      bar rad_reaction --state [--json] [--repo <rid|path>] <target>..\n\n    '--state' reports instead of writing: one line per target, the live markers of ours there and the\n    op that set the newest of them.  It takes no '<to>' and no '--from', and 'markers=-' (no marker\n    of ours is live) is an answer, not an error.  '--json' the same as one object per target, keys\n    'item', 'level', 'target', 'markers' ('emoji', 'op', 'date' each), 'since', 'op'.  A target is\n    named exactly as the writing forms name it, and several targets are read in one call.\n\n    '<to>' is the marker to set: one emoji of the store's allow list, checked before any call, so the\n    '❤️' an emoji picker hands out (heart plus a variation selector) is refused here instead of by the\n    store.  A target is '<issue-id>' (the issue itself), '<issue-id> <comment-id>',\n    '<patch-or-revision-id>' (a patch id is its root revision) or '<patch-or-revision-id> <id>' -- the\n    second id being any reaction-able part of the patch: a comment of the revision, a review, or a\n    comment of a review.\n\n    '<from>' is the one marker the caller expects to be live there: omitted is no expectation at all,\n    the empty string is 'no live marker of mine', anything else must be live -- and an expectation which\n    does not hold fails without writing anything.  Exactly one marker, because the step changes exactly\n    one: it takes '<from>' back and sets '<to>' in the same op, so any other marker standing on the\n    target is none of this call's business, and a '<to>' which is already live costs nothing.  A value\n    naming several markers ('--from \"✅ ❓\"') is therefore a usage error, not a state.  The action list\n    carries the marker to set first and the withdrawal after it, and rad lands that list as a single op\n    -- in one op the actions apply in order and the last one wins, which is why the marker to set comes\n    first.  Its '<from>' is gone and its '<to>' is live afterwards; any other marker stays.  The typed\n    commands are\n    deliberately not used -- 'rad issue react' cannot take a marker back at all ('--undo' is patch only\n    in rad 1.10.3) and the patch commands would be a second code path for the same effect.  An empty\n    '<to>' takes the named markers back and sets nothing, and it needs '--from', because the\n    expectation is what names the markers.\n\n    'rad_topic' opens the topic tree of that work in one action: it mints the name from the\n    branch *class* and the description, resolves the clone and the freshest base, keeps '.worktrees/'\n    out of the main checkout's status and runs 'git worktree add' -- the sequence an agent otherwise\n    reassembles by hand every time it starts a topic:\n\n      bar rad_topic fix \"crash on empty config\"\n      <clone>/.worktrees/fix-crash-on-empty-config  fix-crash-on-empty-config  base=rad/main  head=55d5924\n\n    The name is '<class>-<description>'.  The class vocabulary is read from the branch matchers of\n    the 'git' module ('is_git_*_branch'), so bar's own classes and the gates it hangs off them stay\n    the one source of truth, and a topic's name says what kind of change it is from anywhere -- the\n    worktree, the branch and the patch head.  A name without a class is refused, never guessed.\n    <description> is slugified to '[a-z0-9._-]' and the whole name stays within 40 characters, the\n    limit the Hermes worktree machinery sanitises to; a description which cannot fit is refused, never\n    silently cut.\n\n    The clone is the *main checkout* of the repository '--root' names, or the one the current\n    directory is in -- a linked worktree resolves to its clone, so the call works from inside any\n    topic.  The base is the clone's *trunk*: on this host the trunk is upstream plus the local work\n    which is not published yet, so branching off the remote tip would drop that work.  The 'rad'\n    remote (else 'origin') is therefore fetched first -- to *know* the relation -- and a trunk which\n    is behind upstream is refused, because updating the trunk is its own step with its own gate and\n    never a side effect of opening a topic.  '--base <ref>' overrides the base, '--no-fetch' skips\n    the fetch.\n\n    Neither of the two rules above writes to the main checkout: the tree is created below\n    '<clone>/.worktrees/', kept out of 'git status' by a line in '.git/info/exclude' (never by the\n    tracked '.gitignore'), and 'git worktree add' does not touch the trunk's HEAD.\n\n    'rad_identity' answers what no string comparison can: the identities this repository delegates\n    to, the git authors each one uses, and whether a commit can be tied to one of them -- a name\n    which merely looks like a delegate answers 'verified=no' with the reason.\n\n    'rad_import' is the way in, not a question: on the toplevel of a git repository which is not on\n    radicle yet it runs 'rad init' (public, signing set up) and pushes every branch to the new\n    remote.  Once a 'rad' remote exists it does nothing but say so.\n\n    Running 'rad', reading its tables back, the age of a last event and the COB store are shared with\n    every other module of bar which talks to radicle and live in Bar.d/radicle_lib, which this module\n    requires: 'RAD_BIN' (the command to run instead of 'rad'), the record separators and every 'rad_*'\n    function come from there.  The JSON literals of the '--json' forms below come from\n    Bar.d/json_lib, the one place where bar writes them.\n\n    'RAD_STATE_MAX' is the default of '--max'.  'RAD_TRIAGE_FACTS' names the fact tool 'rad_trust'\n    reads from: the path of the triage fact tool of the caller's own installation, and the only\n    setting for it -- bar cannot know the layout of another machine, so there is no default, and an\n    unset or unexecutable setting is named and answered with 'could not answer' (exit 2) instead of\n    a silently wrong verdict.  Reading rad's tables back needs a character aware shell; bar exports a\n    UTF-8 'LC_ALL' whenever the system has one.\n\n    The rules below are about 'rad', the radicle frontend and the repository it is used in.  Building\n    a radicle installation itself is not one of them (radicle is packaged; the tools below\n    'tools/radicle_*' are their own thing).\n\n    Variables:\n\n      RAD_STATE_MAX (gx)\n        default of '--max': items printed per section\n        Default: ${RAD_STATE_MAX:-10}\n\n      RAD_STATE_PATCHES (ga)\n        one record per patch, see 'rad_state_collect'\n        Default: ()\n\n      RAD_STATE_ISSUES (ga)\n        one record per issue, see 'rad_state_collect'\n        Default: ()\n\n      RAD_STATE_BRANCHES (ga)\n        one record per local branch, see 'rad_state_collect'\n        Default: ()\n\n      RAD_STATE_COB_TIME (gA)\n        '<type>:<id>' -> '<updated> <created> <kind>'\n        Default: ()\n\n      RAD_STATE_CUT (gi)\n        items the last text rendering elided\n        Default: 0\n\n      RAD_STATE_NOW (gi)\n        the timestamp the ages are relative to\n        Default: 0\n\n      RAD_TRUST_STORAGE (g)\n        the node storage of it, once it is resolved\n\n      RAD_TRUST_ITEM_REF (g)\n        the COB of the item asked about, once it is resolved\n\n      RAD_COMMENT_METADATA_AUTHORED (gA)\n        '<rid>|<nid>' -> the commits that identity authored\n        Default: ()\n\n      RAD_METADATA_AUTHORED_VALUE (g)\n        what 'rad_metadata_authored' hands back\n\n      RAD_COMMENT_METADATA_ASSIGNEES (ga)\n        the current assignees of the item asked about\n        Default: ()\n\n    Functions:\n\n      rad_import import a git repository into radicle\n\n      rad_state_charwise\n        - 0 when this shell counts characters, which reading rad's tables needs\n        'rad' pads the cells of its tables by character, so the columns can only be cut at the same\n        offsets when the shell measures the same way.  That is the case in a UTF-8 locale, which bar\n        exports whenever the system has one; in a byte locale a cell holding a non ASCII character\n        shifts everything behind it and 'rad_state' says so instead of reporting it wrongly.\n        (A double width character -- CJK, emoji -- is one column to a character wise shell and two\n        to rad: such a title would shift the columns of its row too.)\n\n      rad_state_ids <table>\n        the ids of the rows of a 'rad' table, one per line\n        A state filter is answered with a table of the same shape and only the ids are taken from\n        it.  An empty result is 'Nothing to show.' or nothing at all -- that is no id, not an\n        error; box drawing characters without a header to cut by are an error (return 1).\n\n      rad_state_has_id <newline separated ids> <id>\n        0 when <id> is one of them\n\n      rad_state_ids_checked <ids> <description> <table>\n        as 'rad_state_ids', but a table it cannot cut is a diagnostic\n\n      rad_state_table_checked <description> <table> <column>..\n        as 'rad_table', but a table it cannot cut is a diagnostic\n        'Nothing to show.' and nothing at all are no rows, not an error; box drawing characters\n        without a header to cut by are.\n\n      rad_state_author <cell>\n        an author cell of a table as one name ('<author>  (you)' -> '<author>')\n\n      rad_state_collect\n        - collect the whole state into the 'RAD_STATE_*' variables\n          patch  id state own author head title updated created kind\n          issue  id state own author labels assignees title updated created kind\n          branch name commit ahead patch\n        Returns 3 when there is no repository to report on (a radicle repository is a git\n        repository with a radicle identity), 1 when rad failed or its output was not understood.\n\n      rad_state_cut <shown> <total>\n        the elision of a cut section, nothing when every item is shown\n\n      rad_state_complete_file\n        - where the complete text of this run is written, see 'rad_state_complete_note'\n\n      rad_state_complete_note <file>\n        say where the complete text went, after a section was cut\n\n      rad_state_text_patches <max> [<state> [<author>]]\n        the patch section as text\n        the author of a patch of ours is us, naming it again would only repeat the identity\n        line; for the others it says who to talk to\n\n      rad_state_text_issues <max>\n        the issue section as text\n\n      rad_state_text_branches <max>\n        the branch section as text\n\n      rad_state_text_head\n        - the repository, identity and head lines of the text form\n\n      rad_state_text <max>\n        the whole state as text, at most <max> items per section\n\n      rad_state_json_patches <state> <author>\n        the selected patches as one JSON object\n\n      rad_state_json_issues\n        - the issues as one JSON object\n\n      rad_state_json_branches\n        - the local branches as one JSON object\n\n      rad_state_json <state> <author>\n        the complete state as one JSON object\n\n      rad_state [--json] [--max <n>]\n        the radicle state of this checkout in one invocation\n\n      rad_patches [--json] [--max <n>] [--state <open|draft|merged|archived|all>] [--author <ours|other|all>]\n        the patches of this checkout, by state and by author\n\n      rad_classes - the branch classes bar defines, one '<class>-' prefix per line\n        Read from the 'is_git_*_branch' rules of the git module, so the vocabulary has one source of\n        truth: the matcher 'fix-* fix/*' contributes the class 'fix-'.  Bar's matches are regexes, so\n        'fix-*' is the prefix 'fix-' followed by anything; the slash forms ('fix/', 'release/') are\n        not classes of a *topic* here -- this host's layout puts a task id behind the slash\n        ('<slug>/<task-id>') -- and the bare trunks ('main', 'master', 'devel') have no prefix at all.\n\n      rad_slug <text>\n        the description as a name slug, empty when nothing is left\n\n      rad_trust_ground_known <name>\n        0 when <name> is one of the grounds\n\n      rad_trust_store <repo>\n        the repository and the node storage of it, in RAD_TRUST_RID/RAD_TRUST_STORAGE\n        Which repository, and where its COBs are: every ground which is a fact about the node's own copy\n        ('owner', 'opened', 'assigned') and a relative threshold need it -- the two facts the fact tool\n        answers do not, and resolving it costs a 'rad' invocation, so it happens only when a ground asks\n        for it.  A repository the node does not hold is *could not answer* (2): nothing about it can be\n        established, not even 'false'.\n\n      rad_trust_owner_nid <storage>\n        the NID of the identity which established the repository\n        The 'owner' ground.  Radicle has no owner field: a repository belongs to its delegates and the\n        identity document (payload, delegates, threshold) names no single owner.  The one identity a\n        'whose repository is this' question can be answered with is the identity that *established* it:\n        the author of the initial revision of the identity document, which is the one 'rad init' gave\n        the project to.  An identity document which is not there, or which has no history, answers\n        nothing -- the ground is 'false' then, never an error.\n\n      rad_trust_total_authored <storage>\n        the commits every radicle identity authored in this repository\n        The denominator of a relative threshold: the commits authored as *some* identity\n        ('<alias>@<NID>'), counted over all refs of the node's copy the same way the fact tool counts the\n        author's own -- which is what lets one setting ('10%') mean the same in a big and in a small\n        repository.  A count, never a failure: no commits means zero.\n\n      rad_trust_item_ref <storage> <id> <type>\n        the COB of the item, in RAD_TRUST_ITEM_REF\n        The item the 'opened' and 'assigned' grounds are about, by its id (short or full) and of the type\n        the caller named.  An id the node does not hold, or one which is the other type, is the caller's\n        error and not 'false': a caller asking about an item this node cannot show cannot act on a no.\n\n      rad_trust_item_nid <storage> <ref> <type>\n        the NID of the identity which opened the item\n        The item's author is the author of the op which *created* it.  A COB is chained onto the identity\n        commits -- and a patch COB onto the patch's own commits -- so the create op is found by its\n        subject ('Create issue'/'Create patch') and confirmed by the manifest next to it, which names the\n        item type: a foreign op in the same chain is not the create op, and an op whose manifest says\n        otherwise is skipped rather than believed.\n\n      rad_trust --repo <rid|path> --author <did|nid|alias> [--issue <id>|--patch <id>] [--grounds <ground,..>] [--threshold <n|n%>] [--dump] [--json] [--second-party] [--requester <did|nid>]\n        the trust verdict of the triage policy; '--second-party' is the actor against '--requester', and against itself without one\n        '--requester' is the reference '--second-party' compares against; on its own it decides\n        nothing, so it is a usage error rather than a value which is silently dropped.\n        The requester has to be a unique identity: an alias is not one (two identities can answer to\n        one), and a comparison against the wrong identity is worse than a refusal.\n        Which grounds are asked for: the named ones, or -- with no '--grounds' -- the rule this helper\n        has always answered (a delegate, or the authored commits reaching the threshold).  '--dump'\n        means all of them, and evaluates nothing.\n        The tool's '--threshold' is an absolute count, so a relative threshold is not handed to it: that\n        comparison is made here, from the same count and the repository's own total.  The tool's own\n        'contributor' verdict is not used either -- the grounds are what decides, once.\n        The tool exits non-zero when it cannot answer (an unresolvable author is one of those), and still\n        prints the JSON which says why -- so the exit code is reported, never interpreted: an empty\n        answer is 'could not answer', anything else is read and the reason travels on.\n        One jq pass, so the text form and the machine form carry the same numbers.  The fields are joined\n        with US (0x1f) and not with a tab: an empty field has to survive the read, and IFS whitespace\n        would collapse it.\n        An author the repository cannot resolve is *not* an error of the tool: it has no data, and that\n        is 'unknown' (exit 2), never 'untrusted'.  Nothing can be established for it, so no ground is.\n        Who asked for the decision.  With no '--requester' it is the actor itself -- no item is read to\n        find an opener, and the answer is 'false'.  A named requester is a NID or a 'did:key:' one and\n        nothing else (checked above), so there is nothing here which could resolve to another identity.\n        'contributed': the commits this identity authored as itself against the threshold -- absolute, or\n        as the share of the commits every identity authored in this repository.  The total is read when a\n        relative threshold compares it, and in '--dump' (the cache call wants the share of a repository,\n        not only of a relative threshold).\n        'owner' and the two item grounds are facts about the node's own copy of the repository, so they\n        need its storage -- resolved once, and only when one of them is asked for.\n        No item named: the two item grounds cannot be established.  They are 'false', never an error\n        -- a caller which asks about them without an item has nothing to decide either.\n        The verdict: the first ground which holds **in the order the caller listed them**, so a caller\n        lists its grounds in the order it wants them reported.\n        'second_party': somebody *other* than the identity which asked for the decision.  '--requester'\n        names that identity; without one it is the actor, so the same identity -- 'false'.\n\n      rad_identity_store <repo>\n        the repository and the node storage which holds it, in RAD_IDENTITY_RID, _STORAGE and _TOPLEVEL (the checkout, empty when the caller is not in one)\n        Everything below is read from the node's own copy: the identity document, the ops and the\n        namespaces all live in the storage, so this resolves which repository and nothing else.\n        A RID names the repository, so the checkout is wherever this runs: it is only ever read\n        for the author line of a commit the store does not hold, and that line proves nothing.\n\n      rad_identity_doc <storage>\n        the accepted identity document: RAD_IDENTITY_OP (its revision op), _OBJECT (the COB it belongs to), _DELEGATES (one DID per line) and _DOC_THRESHOLD; 1 when the store carries none\n        The document is an op whose payload blob is the revision: 'delegates' and 'threshold' are read\n        from the blob, the op and the COB from the store.  The canonical 'refs/rad/id' is the accepted\n        revision; a copy which carries the identity only under its namespaces answers from there.\n\n      rad_identity_authors_pass <storage> <glob>\n        one pass of the author map over the ops of the refs matching <glob>; an identity an earlier pass answered is never overwritten, so the identity COB wins over a comment\n\n      rad_identity_authors <storage>\n        the author lines of every op the store carries: RAD_IDENTITY_MAIL (mail -> name), _SOURCE (nid -> the op its alias was read from) and _SOURCE_MAIL (nid -> its mail)\n        An op is authored '<alias> <alias>@<nid>' and signed with that identity's key -- the author\n        line is the only place a repository records which git authors an identity uses, so the map is\n        built from ops and from nothing else 'git log' would report about a branch.  The identity COB\n        is read first, so 'the op the alias was read from' is the document itself whenever the identity\n        authored one of its revisions.\n\n      rad_identity_is_op <storage> <sha>\n        0 when that commit is a radicle op: its tree carries the manifest which names the COB type, which no branch commit of a checkout does\n        The shape, never the reachability: a patch COB is chained onto the commits the patch was cut\n        from, so 'some COB ref reaches this commit' is true of a branch commit as well and would\n        report an ordinary commit as an op -- and an op signed by the identity which wrote it.\n\n      rad_identity_nid <value>\n        the bare NID of a DID, a NID or an alias; 1 when nothing in this repository names it, 2 when an alias does and more than one identity answers to it\n        An alias is not unique: it is the display name two identities may both use, so the matches are\n        counted and an ambiguous one is refused rather than resolved by the hash order of the map.\n\n      rad_identity_facts <nid>\n        one identity, in RAD_IDENTITY_ALIAS, _ALIAS_SOURCE, _DELEGATE, _THRESHOLD and _GIT_AUTHORS ('<mail>\\t<name>' per line, the author lines the store carries for it)\n\n      rad_identity_text <nid>\n        one identity as one 'identity …' line\n\n      rad_identity_json_identity <nid>\n        one identity as one JSON object\n\n      rad_identity_json_commit\n        - the commit answer as one JSON object, from RAD_IDENTITY_VERIFIED, _HOW, _NID, _COB, _AUTHOR_*, _REASON (the identity document is the same op the identity answers name)\n\n      rad_identity_commit <storage> <sha>\n        whether that commit can be tied to an identity, in RAD_IDENTITY_VERIFIED ('yes'/'no'), _HOW ('identity', 'namespace' or empty), _NID, _COB, _AUTHOR_NAME/_AUTHOR_MAIL and _REASON; 0 verified, 1 not verified\n        Two ways a commit is tied to an identity, and a name is neither of them: it is an op the store\n        accepted ('identity' -- 'rad' verified the payload signature against the author's key), or it\n        is published in that identity's namespace ('namespace' -- the node takes a ref there only from\n        the identity that owns it).  A canonical branch authenticates nothing: it is written by the\n        repository's delegates on behalf of the repository, so a commit which only a canonical ref\n        reaches answers 'no' with that reason.\n        Not in the store: the author line comes from the caller's own checkout, for the reason --\n        and it stays an unauthenticated name, which is what the answer says.\n\n      rad_identity --repo <rid|path> [--did <did|nid|alias>] [--commit <sha>] [--json]\n        the identities this repository delegates to, and whether a commit can be tied to one\n\n      rad_metadata_nid <did-or-nid>\n        the bare NID of an identity\n        'did:key:z6Mk…', 'did:z6Mk…' and a bare NID all mean the same identity; 'rad' prints the\n        first form and a human types any of them.\n\n      rad_metadata_mail_nid <mail>\n        the NID 'rad' writes into a COB op's author mail\n        A COB op is authored as '<alias>@<nid>' (measured: 'cehteh@z6MksaJk…'), so the *actor* of an\n        op travels in that mail.  The namespace the op was read from does not identify it: one COB's\n        chain is replicated into every namespace which holds it, byte for byte the same commits.\n\n      rad_metadata_marks <body>\n        the marker lines at the very start of <body>\n        One '<key>US<value>' record per line of the block.  A marker is '*<key>:<value>' with the key\n        a word of letters, digits, '_' and '-', starting with a letter -- the '<key>:<value>' header\n        form and no fixed vocabulary: what a key *means* is the caller's business (this reads any key\n        back, and only the keys with a registered effect are ever applied).  The block starts at the\n        comment and ends at the first line which is not a marker: a marker quoted inside a reply is\n        prose, not a proposal.\n        A value runs to the **end of the line** and may contain spaces -- what a value may hold is the\n        business of the directive that owns the key (a label follows radicle's rules, which is what\n        'rad_label_name' is for; a trigger takes one of four words), so this reader hands the line out\n        as it is instead of deciding for its callers.  Only the surrounding whitespace is trimmed: an\n        empty value is no proposal and the block goes on, exactly like any other line.\n\n      rad_label_name <value>\n        the label a proposal's value names\n        Radicle's own rule, applied where it belongs: what a label *is* is the applier's business, so\n        the reader hands the line out whole and the label effect normalises the value.  A label is a\n        non-empty string **without whitespace** (the store's 'Label::new': no length limit, no other\n        rejection), and radicle writes a space as a '-', which is why a value carrying one is written\n        the same way here -- each run of whitespace becomes one '-'.  So '*label:two words' proposes\n        'two-words', the label a writer composing that directive is told to emit in the first place.\n\n      rad_metadata_authored <rid> <nid>\n        the commits that identity authored, '-' when unknown\n        Asked once per identity and remembered, so several thresholds cost one lookup.\n        Counted is only what that identity authored *as itself* ('<alias>@<nid>'): the gate has to\n        resolve into a radicle identity, and commits of plain git authors carry no radicle identity\n        and are therefore dropped.  That is why 'commits_as_identity' is read and not the wider\n        'total_authored' -- an identity the command cannot resolve stays '-' and is not trusted.\n        'RAD_TRUST_CMD' is provided by the deployment (a command and its arguments, e.g.\n        'makeitso rad_trust'): where the facts about an identity come from is not this module's\n        business, and a home directory path does not belong into a vendored source file.\n        An identity whose count cannot be read is not trusted -- say so, else a broken facts\n        command looks exactly like a contributor who is below the threshold.\n\n      rad_metadata_trust <rid> <nid>\n        the verdict for one identity, in 'RAD_METADATA_TRUST'\n        Empty when the identity is not trusted: a delegate, an identity named with '--trust did:…',\n        one of the item's assignees, or an identity whose authored commits reach one of the\n        'contributor(<n>)' thresholds.  An identity which is not of the radicle system at all ('z6M…')\n        is never trusted -- there is nothing to authenticate such an op against.\n\n      rad_metadata_body <storage> <op> <type> <subject>\n        the body of one op of that COB\n        Only the op kinds which can carry a body are opened ('Comment', 'Edit comment', 'Create\n        issue', 'Edit root'), and the payload is the blob 'rad' names '0'.  An op of another COB can\n        stand in this chain (rad chains a COB onto the identity commits), so the op has to declare the\n        type the ref carries -- else a foreign comment would be reported against this item.\n        Returns 1 when this op carries no body of that type; the caller skips it either way.\n\n      rad_comment_metadata_ref <storage> <item>\n        the one COB the item names\n        The ref listing is a single call and it is what says which COB the id names.  A COB lives in\n        every namespace which replicated it, so it is keyed by type and id and only the newest tip is\n        kept -- else every op would be read once per copy.\n        Sets RAD_COMMENT_METADATA_REF/_TYPE/_ID.  Returns 3 when the node holds no such issue or\n        patch, 2 when the id matches more than one.\n\n      rad_comment_metadata_assignees <storage> <ref>\n        the item's current assignees\n        The newest 'assign' op of the chain carries the whole set (a radicle op is the new value of\n        its field), so the walk stops at the first one -- walking backwards from the tip is what makes\n        that one read, not one read per op.  Only identities of the radicle system count here: an\n        assignee which is not a DID/NID is dropped like any other entry without identity.\n\n      rad_comment_metadata_collect <storage> <ref> <type> <id>\n        the marker lines of one item\n        Reads the chain of the one item the ref names -- never the whole repository: the ops and their\n        payloads are opened for that COB alone ('rad_comment_metadata_ref' is what resolved which one\n        it is).  Answers oldest first, and inside one second in chain order: the records are laid down\n        in the order the chain is walked and the sort keeps that order (see below).\n        Chronological, and inside one second in *chain order*: the records were laid down as the chain\n        was walked (parent first), so a stable sort on the epoch keeps two markers written in the same\n        second in the order they were written.  The op sha is deliberately *not* a tie break -- it\n        would order a pair by hash, and '--last' would point at the wrong one of the two.\n\n      rad_comment_metadata_newest <storage> <rid> <ref> <type> <id> <key>..\n        the newest block per key\n        The '--last' walk: newest op first, and it stops as soon as every requested key has its newest\n        *trusted* entry -- so the common case opens one payload instead of the whole chain.  The walk\n        skips a marker whose author is not attributable, and that is not a stopper: an older trusted\n        block still applies, else one comment by a passer-by would freeze an item's state.\n        'The newest block carrying that key, taken whole' is meant per key: every line of a key it\n        found is returned (a comment proposing two labels is one application), and a key nobody asked\n        for is left alone.  No <key> at all asks for *every* key, which is what the forward read\n        answers; then the walk cannot stop early, because a key may first appear anywhere in the chain.\n        Answers in the shape the forward read answers in -- oldest first, one\n        '<epoch>US<iso>US<type>US<id>US<op>US<alias>US<did>US<trust>US<key>US<value>' per line.\n        Walked in the order the forward read *ends* in -- newest epoch first, and inside one second the\n        chain's last position -- so 'the newest block' is the last record of that answer and not merely\n        the tip of the chain.  A chain whose dates are not monotone (an op replayed with an older\n        clock) would otherwise let the two paths point at different records.\n        The markers of this op.  A key nobody has answered yet is answered by this op -- with\n        every line of the key it carries, so a comment proposing two labels is one application --\n        and a key a *newer* op already answered is not answered again here.\n        Oldest first, like the forward read: the chain position decides the order, never the date --\n        an add and its undo land in the same second routinely (the walk above is newest first).\n\n      rad_comment_metadata_json - the whole answer as one object\n        Complete, never elided: the entries the filter kept, oldest first.\n        The assignees belong into the answer: they are one of the reasons an entry is trusted, and a\n        caller which sees a surprise wants to know where the trust came from.  Identities travel in\n        the form the radicle tools want ('rad issue assign --add <DID>'), the same one the text\n        output prints: a consumer must never have to re-normalize them.\n\n      rad_metadata_store <name> <rid|path>\n        the repository and the node storage of it, in the 'RAD_METADATA_*' variables\n        Which repository: an explicit one (RID, or a path entered to ask that checkout), else the one\n        this runs in.  Sets 'RAD_METADATA_RID' and 'RAD_METADATA_STORAGE'.  '<name>' is the caller,\n        only for its diagnostics -- 'not a repository here' and 'the node holds no storage' are not\n        the same answer for the operator, and neither is a usage error.\n\n      rad_comment_metadata <item> [--query <label|rmlabel|assign|unassign>].. [--repo <rid|path>] [--trust <spec>].. [--last] [--json]\n        the trusted proposals an item's comments carry\n        the one item the question is about (the only positional argument there is)\n        The trust specs are validated before anything is read: a usage error is not worth a node\n        lookup, and it has to be reported the same way whether or not the node holds the repository.\n        'rad' first: without it nothing can be resolved, and \"the node holds no storage\" would be\n        the wrong answer for the operator to chase.\n        Which repository: an explicit one (RID or path), else the checkout this runs in.\n        Who is trusted: the delegates answer from the identity document, 'did:' names one identity and\n        'contributor(<n>)' is decided from the authored commits the fact tool counts.  The table is\n        module state, because the '--last' walk asks the same question from its own loop.\n        An asked-for contributor threshold needs the facts command: without it every count reads\n        'unknown', which would answer 'nothing is trusted' where the operator asked for trust.\n        The item's assignees are a trust source of their own: assigning an issue to a contributor\n        makes that contributor's proposals count -- so the same query can answer differently before\n        and after an assignment.  That is the design: trust is read when it is asked for, never\n        stored, and it is always an identity of the radicle system ('z6M…') which carries it.\n        '--last' is the newest block *per key* and it is answered from the tip of the chain\n        backwards: what applies is known without reading what it superseded.\n        No radicle identity in the op's author (a git-only author, or a mail which is not\n        '<alias>@<nid>'): there is nothing to authenticate against, so the proposal is dropped\n        -- silently, like every other proposal which is not trusted.\n\n      rad_metadata_rad <repo> <args>..\n        one 'rad' invocation, in the repository the caller means\n        Every applier below does exactly one invocation of 'rad' and this is where it runs: an empty\n        <repo> is the checkout the command runs in, a path is entered, a RID is handed to rad with\n        '-r' (rad then needs no checkout at all).  Nothing is written anywhere else and there is no\n        second write: an applier either makes its one call or it refuses.\n\n      rad_metadata_identity <repo> <did|alias>\n        the DID the write commands take\n        A comment cannot be trusted to spell a DID right, so an alias is resolved before it is written\n        -- by the same fact command the trust gate asks ('RAD_TRUST_CMD', the tool behind 'rad_trust'),\n        for the repository the caller names (or this checkout).  A value which is already a DID/NID\n        needs no command; one which resolves to nothing is refused rather than written as it stands.\n\n      rad_metadata_labels <repo> <type> <id>\n        the labels the item carries now, one per line\n        The mutation target, read fresh and never remembered: the Labels column of 'rad <type> list'\n        is what the item carries, and the applier knows what it has to delete from it.  This is *not*\n        how the chain state is read -- a label written after a proposal is stale -- it is a different\n        question ('what is there' against 'what was proposed').\n\n      rad_metadata_item <item>\n        the 'rad' word and the id of an item as the reader answers it\n        '<type>:<id>' is the form 'rad_comment_metadata' answers with, so it is the form the appliers\n        take; a bare id is an issue, which is the common case (labels and assignments are mostly issue\n        business, and the read side names the type anyway).\n\n      rad_set_label <item> [<add>] [<delete>].. [--repo <rid|path>]\n        one 'rad <type> label'\n        One label added, and any number of labels deleted in the same write: that is what makes an\n        item carry one chain state at a time (the new one in, the previous one out).  '<add>' may be\n        the empty word, which is no label at all -- a removal alone (a lone '*rmlabel:' clearing the\n        chain slot with no successor) is the same write with nothing to add, and at least one of the\n        two has to be given.  A label the item does not carry is rad's business, not this function's\n        -- it hands rad exactly what it was given and reports what it did.\n\n      rad_rm_label <item> <glob>.. [--repo <rid|path>]\n        remove the labels a glob matches\n        The removal half of rad_set_label: what the item carries now is read fresh ('rad <type> list',\n        the mutation target, never remembered) and every stored label matching one of the globs goes\n        out in a single write.  A glob matches the whole stored label, anchored at both ends, so\n        'pr:*' removes 'pr:<id>' and never 'xpr:<id>', and '*' removes every label -- the chain slot\n        included, which is an explicit act.  A glob which matches nothing writes nothing and says so:\n        nothing to remove is an answer, not a failure.  No identity is resolved -- a glob matches\n        labels, never identities.\n\n      rad_metadata_assign <item> <did|alias> [--repo <rid|path>]\n        one 'rad <type> assign' add or delete\n\n      rad_assign <item> <did|alias> [--repo <rid|path>]\n        one 'rad <type> assign --add'\n\n      rad_unassign <item> <did|alias> [--repo <rid|path>]\n        one 'rad <type> assign --delete'\n\n      rad_label_is_protected <label>\n        0 when the label is namespaced, hence protected\n        The one rule of the label model, in one place: a label carrying a ':' is *namespaced* --\n        'pr:<id>', 'issue:<id>' and every other '<namespace>:<rest>' -- and such a label is\n        **protected**: the applier never deletes it implicitly, so it survives every chain\n        transition, and it leaves the item only when a '*rmlabel:' glob or a direct write names it.\n        A colon-free label is a candidate for the *chain slot*, and exactly one of those is kept per\n        item: a new '*label:<colon-free>' replaces the stored one in the same write.\n        The colon and nothing else decides this -- a hyphen or an underscore protects nothing, which\n        is why 'needs-info' is a chain state like any other.  Values only: what matters is the stored\n        label's own text, never where it came from.\n\n      rad_apply_labels <repo> <dry> <entry>..\n        the label state of one item, reconciled and applied\n        The label entries of one item are one *state*: the labels the chain proposes against the\n        labels the item carries, which are read fresh (nothing is remembered between calls).  Three\n        rules are the whole model:\n         * the **chain slot** is the colon-free label ('rad_label_is_protected').  Exactly one is\n           kept, so a proposed colon-free label carries the stored one out in the same write, and a\n           second one in the same block carries the first out in turn -- the block's last one wins,\n           which is the 'a new *label:nopunct overwrites this' rule read within one block.\n         * a **protected** label ('pr:<id>', 'issue:<id>') is never deleted implicitly.  It leaves the\n           item only through a '*rmlabel:' or a direct write, so one the block does not name is\n           reported and left alone.\n         * a **proposal is what a write is made of**.  A block which names no colon-free label says\n           nothing about the slot -- adding 'pr:<id>' is not a statement about the chain state -- and\n           a block which carries no label at all writes nothing at all: what the item carries is\n           reported instead.  Deleting on the strength of an absent proposal would reverse a\n           delegate's write, and a delegate may label by hand with no comment at all; a missing line\n           is ambiguous, never an instruction.\n        An <entry> is one line of the reader's answer as 'key<TAB>item<TAB>id<TAB>value'; the entries\n        which are not labels are not this function's business, except that they name the item.  A\n        label the item already says is not written again, and the label form is 'rad_label_name's.\n        What the chain proposes (every label of the item's block, in the write's own label form) and\n        what the item carries now.\n        No label proposed at all: report what the item carries, write nothing.\n        A protected label the block does not name stays where it is, and says so.\n        What to add, in the order the block wrote it -- and which of them is the chain slot.\n        A block which names no colon-free label says nothing about the chain slot.\n        Nothing to add and nothing to fix: what is left is the slot's own replacement, a removal\n        alone, which the write layer takes as an add-less call.\n\n      rad_apply_from_comments <item>.. [--query <key>].. [--repo <rid|path>] [--trust <spec>].. [--dry-run]\n        apply the newest trusted block per key, as one write per entry\n        The maintenance side of the reader: it asks 'rad_comment_metadata' for the trusted proposals\n        of an item ('--last', which is the state; a block a later one superseded is history) and\n        dispatches every entry to the applier of its key.  The reader owns the trust decision -- an\n        entry whose author cannot be attributed never reaches this function, and it is not reported\n        here because it was never an entry -- and this function owns the effect.  The keys it knows\n        are 'label', 'rmlabel', 'assign' and 'unassign'; any other key is reported and never guessed\n        at.  The removals run first: a block may carry both a '*rmlabel:' and a '*label:' in the same\n        namespace, and the add has to be the later act (otherwise the removal takes the new label out\n        again).\n        The removals first ('*rmlabel:'): a protected label ('pr:<id>', 'issue:<id>') leaves the\n        item only this way, and a block carrying both a removal and an add in the same namespace\n        has to end with the new label in -- which it does only when the removal ran before it.\n        The labels then: they are one state of the item, and the write which changes it may have\n        to delete the chain slot it replaces.  Then everything else in the order it was written.\n\n      rad_reaction_accepts <char>\n        0 when the store would accept it as a reaction\n        One codepoint of rad's allow list (heartwood, 'crates/radicle/src/cob/common.rs').  An emoji\n        picker hands out '❤️' (heart plus a variation selector) and other sequences, and the store\n        refuses those with 'invalid reaction': refusing them here costs no call and no write, and it is\n        the same list -- a character this accepts and the store does not would be a bad promise.\n        U+1F600..U+1F64F, U+1FA75..U+1FA77, U+1FAC0..U+1FAC6, U+1FAE0..U+1FAF8,\n        U+1F300..U+1F5FF, U+2700..U+27BF, U+1F900..U+1F9FF, U+1F680..U+1F6FF\n\n      rad_metadata_reactions <storage> <ref> <kind> <target> <nid>\n        our live markers on one target\n        Reads the live reaction markers of *our* identity on one target, as 'rad cob show --format json'\n        reduces them.\n        <kind> names the part of the document to look at: 'comment' is an issue's thread (its root\n        comment included), 'auto' every reaction-able part of a patch -- a revision, a comment of one, a\n        review, a comment of a review.  <target> may be short: the answer carries the full id.\n        The answer's first line names the target, '|'-separated --\n        '<count>|<kind>|<id>|<review>|<revision>' -- and the lines after it are the live markers of\n        ours, one per line.  Returns 3 when the document does not carry the target.\n\n      rad_reaction_ref <storage> <id>\n        the COB a reaction target names, in 'RAD_METADATA_REF'/_TYPE/_ID\n        Resolves a patch, issue, revision, review or comment id to the COB which carries it.\n        Returns 3 when nothing matches, 2 when the id matches more than one COB.\n\n      rad_metadata_self - the NID of the identity 'rad' uses here\n\n      rad_reaction_action <emoji> <active> <kind> <id> <review> <revision>\n        one 'cob update' action\n        The 'cob update' action for one marker on a target, as the payload rad's own commands write.\n\n      rad_reaction_level <type> <reader-kind> <id> <cob>\n        the level a target really is\n        The level a caller is told about, mapped from the part of the document the reader matched:\n        an issue's root comment is 'root', a comment of the thread or of a revision is 'comment', a\n        revision is 'revision', a review is 'review'.  One mapping, used by both forms of the command,\n        so 'rad_reaction' never answers in two vocabularies.\n\n      rad_reaction_dates <rid> <typename> <cob> <reader-kind> <id> <review> <revision> <nid> <marker>..\n        one JSON object per marker\n        When each live marker was set: '{\"emoji\",\"op\",\"date\"}' per marker, oldest first, with '-' for\n        a marker no op of ours accounts for.\n\n      rad_reaction_cob_index <rid>\n        fill RAD_REACTION_COBS with every COB the node holds\n        The ids the '--state' form needs to tell one target from the next: a word the node holds as an\n        issue or a patch starts a target, and any other word is the id inside it.  Filled once.\n\n      rad_reaction_is_cob <word>\n        0 when the word names a COB the node holds\n\n      rad_reaction_state <repo> <json> <word>..\n        the '--state' form, one line per target\n        Publishes what the write path already reads: the live markers of *ours* on a target, and the\n        op that set each of them.  Read-only -- the store is opened for 'cob show', 'cob log' and\n        'cob list' and nothing else.\n        One target needs no listing: the id is resolved (which walks the store when it names something\n        inside a patch) and that is the answer.  Several do, because only the listing says where one\n        ends and the next begins.\n        The state of that target, as the store reduces it: the first line names what the target\n        really is, the lines after it are the live markers of ours.\n\n      rad_reaction [--from <emoji>] [--repo <rid|path>] <to> <target>.. | <from> <to> <target>.. | --state [--json] [--repo <rid|path>] <target>..\n        one of our reaction markers set, transitioned or withdrawn against the state the caller expects, or '--state' reporting the live ones\n        Compare and swap on the caller's *own* reaction state: one target, one marker, and the write\n        happens only when what the caller expects is what the store reduces.\n        '--state' reports, it does not compare: it takes no '<to>' and no '<from>', and every\n        positional is a target.  The two forms are exclusive because a caller that set a marker while\n        asking for the state would have to be told which of the two answers it got.\n        The '<from> <to>' form is the two leading positionals, the '<to> <target>' form starts with\n        the reaction to set.  What tells them apart is what can be a reaction at all: a target is an\n        id, never an emoji -- and an empty second word is only a '<to>' if that is what it can be,\n        which is how 'rad_reaction <from> \"\" <target>' takes a marker back.\n        The step changes exactly one marker, so '<from>' names exactly one: a value carrying several\n        ('--from \"✅ ❓\"') is refused here -- it is no state a single step could both expect and resolve\n        -- and the one marker is checked against the same allow list as '<to>' before any call.\n\n      rad_topic_json <root> <name> <path> <base> <head> <fetched>\n        one object\n\n      rad_topic <class> <description> [--root <clone>] [--base <ref>] [--no-fetch] [--dry-run] [--json]\n        open the topic tree of an incoming item\n        Body of the 'rad_topic' rule.  Nothing is created before the class, the name, the clone,\n        the base and the collisions have all been resolved, so a refusal leaves no half-made tree.\n        The clone: always the *main* checkout, also when the caller sits inside a topic tree.\n        The base: the clone's trunk -- upstream *plus* the local work, so branching off the remote\n        tip would drop everything this host has not published yet.  The remote is fetched first to\n        *know* the relation; a trunk which is behind upstream is refused, because updating the trunk\n        is its own step with its own gate.\n\n      rad_merge_delegate <rid> <nid>\n        0 when that identity is a delegate of the repository\n        The delegate list is a fact about *today*: a repository can delegate us at any time and take\n        it back, so it is read here, per run, never remembered.  A line is '<did> (<alias>)', so the\n        identity is what stands before the first blank.\n\n      rad_merge_ref <storage> <id>\n        the COB ref of that patch, empty when it is not there\n        A COB lives in the namespace of every identity which replicated it, so the newest copy is the\n        one to read -- else every op would be read once per copy.\n\n      rad_merge_read <storage> <ref> <rid>\n        the head revision of a patch and its newest followup\n        Sets the 'RAD_MERGE_*' facts of one patch:\n          RAD_MERGE_REVISION       the head revision: the newest 'Create patch' or 'Add revision' op,\n                                   an unrevised patch is its own head.  That op's own id is the id a\n                                   review has to name -- 'rad patch list' shows the revision's commit\n                                   instead, which is a different thing.\n          RAD_MERGE_COMMIT         the commit that revision carries\n          RAD_MERGE_ACCEPT         the identity which accepted it, empty when none did\n          RAD_MERGE_STALE          why an acceptance found in the chain does not count, empty when\n                                   none is stale: 'revision' (an older revision was accepted),\n                                   'not-a-delegate', 'review:<verdict>' or 'followup:<kind>'\n          RAD_MERGE_COMMENT        the newest comment of the chain, '<op> <nid>'\n        The acceptance is the newest *followup* of the chain.  A reaction is not one: a marker is\n        bookkeeping -- the reaction loop writes one on every event it reads -- so counting a marker\n        as a followup would void the acceptance of every patch the moment somebody looks at it.\n        Everything else is one: a comment, a new revision, an edit, a lifecycle op.  The walk is over\n        the COB's own first parent chain (a COB commit carries the whole chain as parents), newest op\n        first, and it stops at the head revision.\n\n      rad_merge_target <toplevel> <patch>\n        the branch that patch targets, empty when rad hides it\n        'rad patch list' has no target column and the patch's own revision payload does not carry one\n        either, so the header of 'rad patch show' is the one place which says it.\n\n      rad_merge_upstream <toplevel> <target>\n        the commit the canonical branch holds\n        What a patch is merged into is the *canonical* branch as the last fetch left it.  Merging into\n        the local branch instead would publish whatever else sits on it, which is not this helper's\n        business.\n\n      rad_merge_push <toplevel> <upstream> <head> <target> <patch>\n        merge and push, print the commit\n        'merge-tree' writes the merged tree without a working tree (and is the clean-merge test),\n        'commit-tree' makes the merge commit, the push names that commit: no tree is ever switched, so\n        a checkout somebody is working in stays exactly where it is.  A head the canonical branch\n        already contains is pushed as it is -- a fast-forward, linear history, and the gate run at the\n        head is the post-merge gate.\n        Returns 1 when the push failed and 2 when it went somewhere else: 'refs/heads/<target>' is the\n        canonical branch, and rad has to say that the canonical reference moved -- a push which\n        succeeded without that line landed in our own namespace, which is not a merge.\n\n      rad_merge_abort <toplevel> <storage> <revision> <upstream> <nid> <last-comment>\n        the abort note\n        'does not merge at upstream <sha>' is the comment the abort owes the author, and it goes on the\n        revision it is about.  Once per head revision, not once per run: when our own newest comment\n        already says exactly that, the patch was reported before and nothing is appended again.\n\n      rad_merge_accepted_prs [--dry-run] [--no-fetch] [--max <n>] [--json]\n        merge the patches a delegate accepted and which merge cleanly into their target branch\n        There is no '--repo': the checkout it is run from is the repository, and its canonical\n        branch is the one the patches are merged into.\n        Oldest first: an older topic has waited longest, and a merge into the same branch moves the\n        base under every younger one anyway.\n        Whether it merges at all is read in both modes -- that is the answer a dry run owes,\n        and it writes nothing to find out.\n\n      rad_cob_call <args>..\n        one 'rad' invocation, its answer (both streams) and its status\n        Like 'rad_run', except that the answer is *returned* and nothing is reported: a store failure\n        is itself an answer here -- the four failure cases are told apart by it, and the store's own\n        first line is what 'refused' and 'absent' publish.  'rad' writes its error to stdout, so\n        folding the streams in loses nothing.\n\n      rad_cob_first_line <text>\n        the first line of <text>\n\n      rad_cob_fail <case> <type> <cob> <detail>\n        the answer of a failure and its exit code\n        One JSON line on stdout, so the four cases are told apart by what a consumer already reads.\n        'detail' is the store's own first line where there is one: a paraphrase would hide the\n        sentence the operator has to search for.\n\n      rad_cob_typename <type>\n        the store's type name, 1 when it is not one of the three\n\n      rad_cob_repo <verb> <repo>\n        the repository a read is about, in RAD_COB_*\n        A RID is answered by the node alone ('rad' then needs no checkout); a path -- or nothing,\n        which is the checkout the command runs in -- is named by 'rad inspect --rid'.  A RID the node\n        holds no storage for is 'notfound' (the repository is not here), while a node which cannot\n        even say where its storage is has nothing to read at all ('unreadable').\n        No storage for that RID is 'not found' only when the node itself answers: a profile\n        which is not there, or a node which cannot be reached, has nothing to read at all, and a\n        consumer must not read that as 'the repository is gone'.\n\n      rad_cob_ids <rid> <typename>\n        the ids the store holds of one type, one per line\n\n      rad_cob_cob_rows <rid> <storage> <typename>..\n        '<type>\\t<id>\\t<tip>\\t<copy>' per COB the store lists\n        The join 'list' and 'tips' both publish, so the two cannot disagree about a copy.  The id set\n        is the store's ('rad cob list', one call per type): a COB this node created carries no ref and\n        'for-each-ref' shows nothing for it, which is why the listing is asked at all.  The tips are\n        one 'for-each-ref' over every type at once ('rad_cob_state_tips'), first wins per COB after\n        its newest-by-date sort, and a COB no ref names keeps an empty tip -- printed, never dropped.\n        Exit 0 answered, 4 no type the store could answer, 5 the storage cannot be listed.\n\n      rad_cob_tip <ops-json>\n        the id of the newest op rad printed, empty when there is none\n\n      rad_cob_copy_note <storage> <type> <id> <op_id>\n        name the copy a read landed on\n        Sets 'RAD_COB_COPY' to the namespace a read landed on, and says it on stderr -- always, not\n        at some verbosity: it is the answer to 'which copy is this', and a consumer that derives a\n        verdict from a chain behind the newest copy has to be able to see that.  The op's id is the\n        commit a namespace ref points at, so the tip names its copies; when the newest copy is a\n        different tip the answer is behind it, and that is a loud line, not a note.\n\n      rad_cob_list see the module header\n        One 'rad cob list' per type and one 'for-each-ref' for all of them: the copy is read off the\n        same join 'tips' publishes, never off a read per COB.\n\n      rad_cob_tips see the module header\n        The heads alone: every COB the store lists with the newest op of its newest copy, so a\n        consumer holding an anchor per COB learns which ones moved without reading a chain.  One\n        'rad cob list' per type asked plus one 'for-each-ref', whatever the number of COBs.\n        'tip' is always a string: \"\" says the store lists the COB and no ref of this storage names\n        it -- a consumer reads that as \"nothing to read yet\", never as \"gone\", and never as an\n        object it may act on.\n\n      rad_cob_ops see the module header\n        The store's own listing decides 'not found' from 'absent': an id it lists whose chain\n        does not materialise is absent, an id it does not list does not exist.  That listing\n        is asked only here, on the failing path: a copy under some namespace proves the store\n        carries the object, and only an object no ref names -- one this node just created --\n        needs the listing itself.\n        'rad cob log' prints newest first; a chain is read oldest first.  '--from' and '--until'\n        narrow the same chain and both bounds are inclusive (measured on rad 1.10.3), so the window\n        is reversed rather than re-derived.\n\n      rad_cob_document see the module header\n        One offset for the whole document, taken once: the store's timestamps carry no zone, and one\n        call per timestamp would cost a process each.  '$zone' is what 'date +%z' prints, so a date\n        here reads like 'date --iso-8601=seconds' renders it.\n\n      rad_cob_state_tips <storage> <typename>..\n        '<type>\\t<id>\\t<tip>\\t<copy>' per COB\n        One listing for every type asked for; the copy named is the one whose tip is newest.\n\n      rad_cob_state_ops <storage> <tip>..\n        one JSON object per op, with what its tree holds\n        One JSON object per op of every chain, in one 'git log', each with its diff against its first\n        parent.\n\n      rad_cob_state see the module header\n        The verb: parse, name the repository, take the copies, read the chains, reduce.\n        '--type' is a set: each value may name several types, and 'all' names both kinds a reading\n        of state is made of.  'id' is refused: an identity COB has no markers, comments or revisions.\n        '--since <cob>:<op>' skips a COB whose head is still <op>; both sides are prefixes.\n        An empty repository is an answer: nothing moved, nothing to report.\n        An empty answer is no line at all, never a blank one: a consumer reads zero records as\n        \"nothing moved\".\n\n      rad_cob list|tips [--repo <rid|path>] [--type <type>].. | ops|document --repo <rid|path> --type <type> --object <cob> [--from <op>] [--until <op>] [--bodies] | state [--repo <rid|path>] [--type <type>].. [--since <cob>:<op>]..\n        the COB store as one interface, one verb per question\n        Every verb's answer is a JSON object -- including the one that names a failure -- so jq is a\n        dependency of the family, checked once, before a verb spends a call on the store.\n\n      rad_rebase_show_field <show> <field>\n        one header value of 'rad patch show'\n        '<show>' is the whole output.  The header line reads '│ <Field>   <value>' and the value is\n        padded to the box, so it is cut exactly like a table row: the field name is the cell 'rad_line'\n        drops, and what is left is the value.  Never parsed from the document -- a patch's own\n        revision payload carries no branch, and the COB's 'target' field is 'delegates' (the *review*\n        target, who is asked to review), not the branch.  'rad patch list' has no such column either.\n\n      rad_rebase_live_comment <storage> <ref>\n        the newest comment of the chain, and its body\n        Sets 'RAD_REBASE_COMMENT' (the op id) and 'RAD_REBASE_BODY'.  Empty when the chain has no\n        comment at all.\n        Redaction-aware, and it has to be: a COB chain is append-only, so a directive the loop has\n        already consumed is still in the history -- its deletion is a 'Redact comment' op naming it --\n        and a read that trusted the text would call a spent '*trigger:rebase' live and never ask that\n        patch again.  Walked newest first, so a redaction is always read before the comment it removes.\n\n      rad_rebase_ask [--after <patch>] [--branch <branch>] [--onto <sha>] [--dry-run] [--no-fetch] [--json]\n        ask the pending patches of a branch to re-cut themselves after a merge landed\n        The branch: what the merge landed on, never a guess.  '--after' asks the merged patch itself\n        (rad resolves its upstream, else the repository's default branch), '--branch' names it\n        outright, and with neither it is the default branch the node's storage points at -- a\n        delegate may make any branch the default, so the name decides nothing and is not inspected.\n        A merge that did not land is not a merge.  The patch has to read 'merged' (above) *and* the\n        commit it was merged as has to be in the branch now: a push that failed, a merge into another\n        branch, or a landing that invented its own base is not something the siblings are asked about.\n        The patch that just landed is no member: it is closed by the merge, and were it still\n        open its own head is the thing that landed -- asking it to re-cut would move what the\n        branch now holds.  Compared as a prefix, because the two sides are read from different\n        places: the listing prints seven hex, '--after' is the full id the caller was handed.\n        The write needs the full 40-hex id: 'rad patch list' prints seven and 'rad patch\n        comment' resolves only the whole object id, so a directive posted under the listing's id\n        is refused for every member -- measured after one landing, 30 refusals and not one\n        directive.  The header carries it, so it costs no extra call.\n        Another author's patch is not ours to publish: a revision only ever goes into the\n        namespace of its own author, so a directive there could not be worked.\n        A directive already standing is never sent twice: the sole line is always the\n        same text, so a second one is a duplicate op, and the loop answers it as such.\n        The one thing that may not be guessed at: a write under a short id is refused,\n        and the refusal reads like a repository refusing us rather than a wrong id form.\n\n      rad_branch_checkout <tree> <branch>\n        the checkout which has <branch> checked out, empty when none\n\n      rad_branch_sync [--branch <branch>] [--dry-run] [--json]\n        fast-forward a local branch to its remote tip, and write nothing when that would not be clean\n\n    Rules:\n\n      rad_import\n        Imports the repository at the toplevel.  Not a question but the way in: it changes the repository\n        (a new remote, the branches pushed) and says what it did.\n\n      rad_state\n        The radicle state of this checkout: repository, identity, patches, issues and the branches\n        which still carry local work.\n\n      rad_patches\n        The patches of this checkout, by state and by author.\n\n      rad_trust\n        The trust verdict of the triage policy; exit 0 trusted, 1 untrusted, 2 could not answer.\n\n      rad_identity\n        The authenticated map of this repository.  The identities are the delegates of the accepted\n        identity document; an alias and its git authors come from the ops the store carries, which are\n        authored '<alias> <alias>@<nid>' and signed with that identity's key -- and every answer names the\n        op it was read from ('identity_cob', 'alias_source').  '--did' resolves one identity, a DID, a NID\n        or an alias; without it every delegate is answered.  '--commit <sha>' is the other direction:\n        'verified' when the commit is an op the store accepted ('how=identity') or is published in that\n        identity's namespace ('how=namespace', a ref the node takes only from the identity which owns it).\n        A commit whose author line merely looks like a delegate answers 'verified=no' with the reason --\n        the forgery this is for -- and authorship stays apart from delegation: an op of an identity this\n        repository does not delegate to is verified and answers 'delegate=no'.  Exit 0 tied to an\n        identity, 1 not verified, 2 usage, 3 no such repository, identity document or identity.\n\n      rad_comment_metadata\n        Who is trusted is read when the question is asked, from the repository (its delegates), from the\n        item (the identities it is assigned to) and from the command line ('--trust'); 'contributor(<n>)'\n        asks 'RAD_TRUST_CMD' for the commits that identity authored as itself -- the gate resolves into\n        radicle identities, commits of plain git authors are not counted.  Only identities of the radicle\n        system carry trust -- an op whose author or assignee is not a DID/NID is dropped, silently, and\n        'RAD_TRUST_CMD' being unset is only an error when a contributor threshold was asked for.  A\n        proposal which is not trusted yet can become trusted later (a delegate assigns the item to its\n        author), so the answer to the same query is allowed to change between runs.\n\n      rad_topic\n        Creates '<clone>/.worktrees/<class>-<description>' on the branch of the same name; exit 0 created,\n        1 refused (the tree or the branch exists, the name is too long), 2 could not answer (usage, an\n        unknown class, not a git repository, git failed).\n\n      rad_reaction\n        Compare and swap on the caller's *own* reaction state: a reaction is private per identity and can\n        never be delegated, so there is no trust question here.  '<to>' is the marker to set (one codepoint\n        of the store's allow list, checked before any call), '<from>' what the caller expects to be live\n        there -- omitted is no expectation at all, the empty string is 'no live marker of mine'.  An empty\n        '<to>' withdraws '<from>' and sets nothing: patch level only, and it needs '--from', because the\n        expectation is what names the marker.  A target is '<issue-id>', '<issue-id> <comment-id>',\n        '<patch-or-revision-id>' (a patch id is its root revision) or '<patch-or-revision-id> <comment-id>'.\n        '--state' reports instead of writing, one line per target: the live markers of ours and the op\n        that set the newest, so a consumer can age what it parked.  It takes no '<to>' and no '--from', and\n        'markers=-' is a state (exit 0), never a failure.  Exit 0 applied (or reported), 1 the expected\n        marker is not what the store reduces (nothing was written) or the store could not be read,\n        2 could not answer (usage, a reaction the store would not accept, a missing target),\n        3 the node holds no such repository, item or revision.\n\n      rad_cob\n        Exit 0 answered, 1 the store refused (its own first line is 'detail', unchanged), 2 usage, 3 no\n        such repository, type or object, 4 the store lists the object and nothing materialises, 5 the\n        store or the node cannot be read.  Each failure is one JSON line on stdout, so a consumer which\n        reads the exit code and stdout alone tells them apart.\n\n      rad_merge_accepted_prs\n        The acceptance is read off the patch's own chain -- a delegate's 'review' with 'verdict: accept'\n        naming the head revision, with nothing but reactions after it, else it has to be accepted again --\n        and the merge is a push into the canonical branch, which only a delegate may make: an invocation\n        under any other identity refuses before it writes.  '--dry-run' answers what is ready and writes\n        nothing.  Exit 0 answered, 1 refused or could not answer, 2 usage, 3 no data.\n\n      rad_rebase_ask\n        A *sender*, nothing more: it merges nothing, re-cuts nothing and opens no worktree.  It posts one\n        comment -- the sole line '*trigger:rebase' -- on every pending patch of ours whose branch has just\n        moved, so the re-cut materializes in that patch's own round (its own claim, worktree, session and\n        gate).  '--after <patch>' is the merge path: the patch has to read 'Status merged' and the commit\n        it was merged as has to be in the branch now, else nothing is asked and the exit code says so --\n        a failed or unreadable merge is never followed by a sweep.  The branch is the merged patch's\n        'Target' (rad resolves the upstream, else the repository's default branch, whatever it is named);\n        '--branch' names one outright when there is no merge to point at, and '--onto' overrides the\n        branch tip the members are measured against.  A patch is skipped when its 'Target' is another\n        branch, when it is another author's (a revision only goes into the author's own namespace), when\n        its head already contains the tip, and when a live '*trigger:rebase' already stands -- a consumed\n        one is redacted in the chain and does not count.  Exit 0 nothing failed, 1 a refusal or a failed\n        write, 2 usage, 3 no data.\n\n      rad_branch_sync\n        The step 'rad_topic' refuses without and nothing else runs: a branch which is behind its remote tip is moved forward here, and nothing but the fast-forward happens -- '--dry-run' answers what it would do and moves no ref, a branch carrying commits the remote does not have is reported 'ahead' and left alone, a divergent one 'diverged', and a tree holding uncommitted work refuses the update instead of working around it.  The branch is the canonical one the node's storage names ('--branch' names another), the remote is 'rad' when the clone has one else 'origin', and the tip is asked of the remote rather than read off a tracking ref.  Exit 0 updated, current, ahead or a dry run, 1 refused or could not write, 2 usage, 3 no data.\n\n      rad_rm_label\n        The removal half of the write layer's label effect: the labels the item carries are read fresh\n        ('rad <type> list', the mutation target, never remembered) and every one matching a glob goes out\n        in a single 'rad <type> label --delete' invocation.  A glob matches the whole stored label,\n        anchored at both ends, with '*' and '?': 'pr:*' removes 'pr:<id>' and never 'xpr:<id>', and '*'\n        removes every label -- the chain slot included, an explicit act.  A glob matching nothing writes\n        nothing and is reported: nothing to remove is an answer, not a failure.  No identity is resolved,\n        a glob matches labels and never identities.  Exit 0 removed (or nothing matched), 1 the write 'rad'\n        refused or could not answer, 2 usage.\n\n  rad_rules - The rules of the 'rad' module which everything else may depend on before a rulefile is read:\n    the cheap guards of a radicle checkout and the hook into 'activate'.\n\n    'bar' requires every '*_rules' file on every invocation, so what lives here is a predicate or a\n    hook and nothing else: the heavy bodies, the tables and the rendering are in 'Bar.d/rad' and\n    'Bar.d/radicle_lib', which are loaded when a 'rad_*' rule actually runs.  A '_rules' file which\n    carries neither a clause nor a guard is dead weight parsed on every run.\n\n      is_rad_available   the command bar runs ('RAD_BIN', else 'rad') is here at all\n      is_rad_repository  that command answers for this checkout ('rad inspect --rid')\n      is_rad_toplevel    we are at the toplevel of such a repository\n      rad_activate       keep '.worktrees/' out of 'git status' (hooked into 'activate')\n\n    The predicates answer about the same command the helpers run -- 'RAD_BIN' when it is set, 'rad'\n    otherwise -- without pulling the library in for it.  A predicate answers *itself*: it is 0 or 1\n    and a missing command is a 1, not a skip.  A rule which only wants the gate writes it with a\n    question mark ('is_rad_repository? something') and is skipped when it does not hold.\n\n    The predicates are deliberately *not* registered as repertoire helpers: a helper answers a\n    retrieval question with an output, a predicate prints nothing and is only ever the dependency of\n    another rule ('Bar.d/helpers_rules' documents that split).\n\n    CONSIDERED AND NOT WIRED -- nothing consumes them yet; each becomes a clause here when something\n    does:\n\n      'rad sync --announce' in a publish hook -- measured on this host: every 'git push rad' and\n        every 'rad patch edit' syncs with the seeds by itself ('Synced with 3 seed(s)') and a push\n        announces the repository ('rad ls' flips 'local' to 'public'), so such a hook would only\n        repeat what the push already did and pay a network round trip for it;\n      a 'fetch_resources' clause ('git fetch rad') -- it would put the network on a path which\n        'lints' runs through on every invocation;\n      an 'agent' clause failing when 'a local branch carries commits no patch exposes' -- true for\n        every commit in progress, far too noisy for the check an agent runs after each change.\n\n    Rules:\n\n      is_rad_available\n        Succeeds when the command bar runs is available: 'RAD_BIN' when it is set, else 'rad'.\n\n      is_rad_repository\n        Succeeds when that command answers for this checkout, that is when it is a radicle repository.\n        A missing command is a *no* here, not a skip and not the 127 of a shell that cannot find it.\n\n      is_rad_toplevel\n        Succeeds when we are at the toplevel of a radicle repository.\n\n      rad_activate\n        Maintain a radicle checkout for bar: keep '.worktrees/' -- where 'rad_topic' puts the topic trees\n        -- out of 'git status', through '.git/info/exclude'.  Setup, not a question: it writes the line\n        once and is silent when it is already there.  The target is the *clone* ('rad_main_checkout'), not\n        the tree bar was called from: a topic worktree has a private git directory, and an exclusion\n        written there leaves the checkout's own status untouched.\n\n  radicle_lib - Shared radicle plumbing: run 'rad', read what it prints, and emit the JSON literals.\n\n    The radicle domains of bar -- the state of a checkout, the flow around an incoming item, the way\n    a repository gets onto radicle (all of them 'rad_*' rules of Bar.d/rad) -- ask the same small\n    questions about the tool and its data.  The answers live here once, named after the command they\n    belong to:\n\n      rad_run                run '$RAD_BIN' and carry its answer and its exit code\n      rad_path               where the node keeps its storage ('rad path')\n      rad_cobs_time          when and how the last event of a patch or issue happened, read from\n                             'refs/namespaces/*/refs/cobs/xyz.radicle.<type>' below that storage\n      rad_cob_copies         every namespace copy of one COB, newest first ('<date> <tip> <nid>')\n      rad_cob_newest         the newest of those, and 'rad_cob_nids' the namespaces carrying a tip\n      rad_age                an epoch as a short age ('6m', '2h', '3d', '1y')\n      rad_line/rad_columns/rad_table\n                             cut the padded, boxed tables 'rad' prints by the names of their\n                             columns\n      rad_main_checkout      the main checkout of the clone a (linked worktree) path is in\n      rad_exclude_worktrees  keep '<clone>/.worktrees/' out of 'git status'\n\n    The JSON literals of the '--json' forms are not rad's business: every module of bar which emits\n    one asks Bar.d/json_lib, which this library requires ('json_string', 'json_number', 'json_bool',\n    'json_list').\n\n    This module defines no rules.  It is the backing library of the 'rad' word, required by the\n    module which carries those rules ('Bar.d/rad') -- a rule name like 'rad_state' makes\n    'rule_autoload' look for a module 'rad_lib', which this file is not: the library of the radicle\n    domain is named after the protocol, not after the word.  Nothing here goes to the network and\n    nothing guesses: a function which cannot answer returns non-zero ('rad_run' tells 'no radicle\n    repository here' (3) from 'rad failed' (1), see there), and the callers own their diagnostics.\n\n    'RAD_BIN' names the command to run instead of 'rad' (the tests point it at a fake).\n    'RAD_US' is the field separator of the records these functions return and take\n    ('rad_table' hands its cells over that way, US for the same reason Bar.d/helpers_rules uses it:\n    a field may be empty and a space would collapse it), and 'RAD_RS' is the field separator\n    of a 'git for-each-ref' line.  Reading rad's tables back needs a character aware shell; bar\n    exports a UTF-8 'LC_ALL' whenever the system has one.\n\n    Variables:\n\n      RAD_BIN (gx)\n        the command to use instead of 'rad'\n        Default: ${RAD_BIN:-rad}\n\n      RAD_US (gr)\n        the field separator of the records 'rad_table' returns\n        Default: $'\\037'\n\n      RAD_RS (gr)\n        the field separator of a 'git for-each-ref' line\n        Default: $'\\011'\n\n    Functions:\n\n      rad_run <args>..\n        run '$RAD_BIN' and print what it wrote\n        Fails with 1 and a diagnostic on stderr when rad failed, with 3 when the failure was that\n        there is no radicle repository: stderr is folded into the output so both can be told apart.\n\n      rad_path - the radicle home of this node, empty when 'rad path' does not answer\n        Memoized: the COB reader asks for it once per object type, and every caller within one run\n        wants the same answer.\n\n      rad_cobs_time <type> <rid> <array>\n        when and how the last event of every <type> happened\n        The kind and the time of the last event of every patch and issue into the associative\n        array <array> named by the caller, as '<type>:<id>' -> '<updated> <kind>'.  Every namespace\n        of the node storage keeps a copy of a COB, so the newest commit over all of them is the\n        last event and its subject names its kind ('Comment', 'Merge revision', ...).  A storage\n        which is not there is not an error, the times stay empty then.\n\n      rad_cob_copies <storage> <type> <id>\n        every namespace copy of one COB, newest first\n        Every namespace of the node storage keeps its own copy of a COB, and a peer's copy can be\n        ahead of ours.  One '<date> <tip> <nid>' line per copy, in one stable order -- the commit\n        date (unix) descending, the namespace ascending to break a tie -- so 'the newest' is a\n        decision a caller reads off the first line and never a wildcard match.  A storage which\n        cannot be read, or which carries no copy, answers nothing and succeeds.\n\n      rad_cob_newest <storage> <type> <id>\n        the newest copy of one COB, '<date> <tip> <nid>'\n        The one line 'rad_cob_copies' put first, and nothing when there is no copy.  Read into an\n        array rather than piped through 'head': a consumer of a pipe that closes early fails under\n        'pipefail' in the caller, and this answer is not worth such a surprise.\n\n      rad_cob_nids <storage> <type> <id> <tip>\n        the namespaces whose copy carries <tip>\n        Which copy an op chain came from: the op's id *is* the commit the namespace ref points at,\n        so the namespaces sharing that tip are the copies a read of it could have landed on.  One\n        nid per word, with a trailing space; empty when no copy carries that tip.\n\n      rad_age <now> <epoch>\n        a short age ('6m', '2h', '3d', '1y'), '-' when there is no epoch\n\n      rad_line <line>\n        a table line without the box edges, the state marker cell and the padding\n        '│ ●  a1bd13a  title  ...  │' -> 'a1bd13a  title  ...'.  The marker cell is a single token\n        ('●' or '✓', whatever rad prints there) and is dropped: the columns behind it then start at\n        the same offset in the header line and in every row, which is what 'rad_table' cuts by.\n\n      rad_columns <header>\n        the start offset of every column of a normalized table header\n        A column starts where a run of two or more spaces ends and content follows (plus the first\n        one): the two spaces are the separator between two cells, the padding of a cell is never\n        part of its neighbour.  Neither a name nor a value is needed for this, the header line\n        carries the layout itself.  The offsets are space separated.\n\n      rad_table <table> <column>..\n        the named cells of every row of a 'rad' table, US separated\n        The columns are located by name in the header line (see 'rad_columns' for where a column\n        starts), then every row is cut at the same offsets -- up to where the next column begins,\n        so that a requested cell never carries the ones behind it.  Cut and not split: a cell may\n        itself contain two spaces (a truncated author reads '<author>  (you)'), which splitting on\n        whitespace would tear apart.  Every requested name has to exist in the table, otherwise\n        this returns 1 and the caller reports that rad's output could not be understood.\n\n      rad_main_checkout [tree]\n        the path of the main checkout of the clone '<tree>' is in\n        The first entry of 'git worktree list' is the clone itself, every further one is a topic tree\n        below it.  A caller which writes into the clone's own '.git/' -- 'rad_topic' creates the\n        trees there, 'rad_activate' keeps them out of its status -- must name the *clone*, never the\n        toplevel it happens to be called from: a linked worktree has a private git directory of its\n        own ('<clone>/.git/worktrees/<name>'), so an exclusion written for it does nothing for the\n        checkout whose 'git status' shows the trees.  A path which is no repository answers itself,\n        the caller's own check is what reports that.\n\n      rad_exclude_worktrees <clone>\n        keep '<clone>/.worktrees/' out of 'git status'\n        The line goes into '.git/info/exclude', never into a tracked '.gitignore': the main checkout\n        has to stay byte-identical, and a tree which exists is not a fact about the project.\n\n  release - Rules for software releases\n\n    The 'release' module implements a (opinionated) release workflow. Making releases starts\n    on a git toplevel in a development or main branch, a devel branch does not need to be\n    clean.  For each release a new branch/worktree based on the last commit is created. All\n    release work will be done in that branch/worktree. This can be a interactive process, when\n    release_tests fails one can correct things and commit them until the tests pass. The\n    actual release procedure then evaluates 'release_prepare', 'release_commit', 'release_tag'\n    and 'release_publish'.  Finally 'release_postprocess' and 'release_cleanup' are evaluated.\n\n    Any non published release can be aborted/deleted.\n\n    This only defines the bare workflow the actual implementation hooks into the\n    'release_rules'.\n\n    We still make a few fixed assumptions:\n     - Versioning is based on semver\n     - Releases are called 'release'\n     - The main branch can be named 'main' or 'master'\n     - Development branches matching 'devel' 'devel-*' or 'devel/*'\n\n    The worktree tracks its state in a local git config bar.release.state:\n     - start:\n       The worktree is created but not tested yet.\n     - tested:\n       All test passed.\n     - changelog:\n       generated the changelog and committed it.\n     - ready:\n       The worktree/branch is ready for publishing\n     - published:\n       uploads succeeded.\n     - done:\n       The release finished.\n\n    Further following git config variables are set:\n     - bar.release.startbranch:\n       Branch this release initiated from (usually main or devel).\n     - bar.release.version:\n       The (semver) version of this release.\n\n    Release Configuration and Workflow\n\n    Every release starts in its own git worktree. All testing, and fixes necessary for the\n    release are made in that worktree.\n\n    Configuration of release workflows is done by extending the rules from 'release_rules'.\n    Many other '*_rules' modules already hook into these rules and do the 'right thing'.\n    The parts that can configured in Barf are:\n      - release_prepare:\n        Any extra preparation for releases. 'cargo_rules' already bumps the version.\n      - release_tests:\n        Whatever tests are necessary for making a software release. 'lints' and 'tests' are\n        already defined dependencies. Anything more needs to be added in Barf.\n      - release_preprocess:\n        Any final work that needs to be done before publishing. This should not change the\n        project files anymore. This is the place to do git tags, merge/fast-forward branches\n        or switch over to other worktrees. For convenience the 'release_as_*' rules exist to\n        do this work.\n      - release_publish:\n        The actual publishing part. May need access to the internet.\n        The 'git_rules' and 'cargo_rules' already hook into this.\n      - release_postprocess:\n        Any work to be done after the publishing. Like rebase work/devel branches on the\n        release and so on.\n      - release_cleanup:\n\n    Functions:\n\n      release_is_patch\n        checks whenever the current worktrees release is a patch release\n\n      release_state_matches [[!]statematch]\n        Check if the current state matches (does not match with '!').\n\n      release_into <target_version>\n        switch over to another existing release worktree, reset it to the current release state\n        We prepare releases in dedicated worktrees. This would leave a lot worktrees behind.\n        This provides the facility to coalesce worktrees to keep only major, majorpre1x, or majorminor versions.\n\n      release_into_major\n        Transfer over to major version, for workflows that keep 'release-x' worktrees and branches\n\n      release_into_majorpre1x\n        Transfer over to major version with 0.x major semantic, for workflows that keep 'release-0.x' and 'release-x' worktrees and branches\n\n      release_into_majorminor\n        Transfer over to major.minor version, for workflows that keep 'release-x.y' worktrees and branches\n\n      release_worktree_rename <version>\n        rename worktree and cd into that\n\n      release_worktree_rename_major\n        Rename worktree to'release-x' with major version only.\n\n      release_worktree_rename_majorpre1x\n        Rename worktree to'release-x[.y]' with 0.x major semantic.\n\n      release_worktree_rename_majorminor\n        Rename worktree to 'release-x.y' with major.minor.\n\n    Rules:\n\n      release\n        Makes a software release. Takes an optional version number starting with a digit or a\n        name for the generating the version by 'release_generate_version_hook' as argument.\n        When no argument is provided it defaults to 'auto'.\n\n      release_as_major\n        To be used from release-preprocess. Creates or resets the release worktree to release.x major.\n\n      release_as_majorpre1x\n        To be used from release-preprocess. Creates or resets the release worktree to release.x[.y] major with 0.x major semantic.\n\n      release_as_majorminor\n        To be used from release-preprocess. Creates or resets the release worktree to release.x.y major.minor.\n\n  release_rules - The rules called by the release rule\n\n    Rules:\n\n      release_prepare\n        Update manifest, bump versions, generate changelog an other versioned artifacts etc..\n        Should leave the tree in a uncommited state. Note that 'build_versioned_artifacts' is\n        already a dependency here.\n\n      release_tests\n        All tests and lints to run for a software release.\n\n      release_changelog\n        Create the changelog and commit it, may include tags etc.\n\n      release_preprocess\n        Local actions to be done before the release, rebase main/devel on it etc.\n\n      release_publish\n        Deploy the release, upload it to servers, git push or whatever necessary. Since network\n        operations can be fallible this can be restarted.  Should start with them least critical\n        (git push) and finally publish it to registries.\n\n      release_postprocess\n        Local actions to be done after the release.\n\n      release_cleanup\n        Final cleanup for the release, clean/delete worktrees etc\n\n      release_generate_version\n        Canonicalize version names. Queried when the the version to 'release' does not start with a\n        digit or was not given. Other modules should plug in here. Names are 'auto', 'major',\n        'minor', 'patch'. Custom rules may support more names such as 'prerelease', 'alpha', 'beta'\n        and so on. 'auto' is the default for 'release'.\n\n      release_current_version\n        Query the current version of this project.\n\n  rule_lib - The library that implements rules, always loaded\n    Dependency jobs -- '+' starts a run of dependencies concurrently\n\n    Variables:\n\n      BAR_KEEP_GOING (gx)\n        Controls keep-going mode: 0 or 1 stops at first failure (default), numbers >= 2 allow that many failures before stopping, non-numeric values (yes/true/etc) continue until exhaustion. In keep-going mode, pure clauses continue executing after failures though failures are still propagated. Non-pure or conclusive clauses halt keep-going within the current rule.\n        Default: ${BAR_KEEP_GOING:-1}\n\n      BAR_PARALLEL (g)\n        How many dependency jobs may run at once. 1 (the default) starts nothing early and is the serial evaluation.\n        Default: ${BAR_PARALLEL:-1}\n\n    Functions:\n        See RULE DEFINITION SYNTAX in help for details\n\n      rule_install_dep <name>\n        makes what a clause dependency names reachable\n        Called by :func:`rule_install_deps` while BAR_INSTALL_DEPS is set.  A dependency is\n        resolved the way the evaluator resolves it, only without failing: this runs at *definition*\n        time, where the rule that names the dependency is not evaluated at all, so a dependency\n        nothing provides must not stop the walk (it is a dependency of a rule this project never\n        calls, or one of the optional ones).  Loading the module a dependency lives in may define\n        further rules, which walk their own dependencies in turn.\n\n      rule_install_deps <body> [deps..]\n        loads what a clause's body and dependencies need\n        Called from :func:`rule` while BAR_INSTALL_DEPS is set.  Every dependency is made\n        reachable (and the module it lives in loaded, transitively) and the body is walked line by\n        line: a line which is a 'require' command is executed.  Nothing is evaluated, this only\n        makes sure the rules, commands and modules a clause names exist.\n        A dependency with a '??' suffix is one the evaluator only runs *when it exists* -- forcing\n        it here would install modules a project never uses, so it is skipped.\n\n      newrule <name:> ...\n        Creates a new rule, deletes the existing one.\n        Used to replace already existing rules/clauses.\n\n      rule_delete <name>\n        Deletes a rule (and all it clauses).\n        Can only be used when the rule was not evaluated yet.\n\n      rule_rename <oldname> <newname>\n        renames a rule\n        Can only be used when the rule was not evaluated yet.\n\n      clause_local [var[=value]|=var..]\n        Makes scalar variables local to a clause.\n\n      rule_eval <name> [args..]\n        Evaluates a rule\n        Normally this is used by bar itself but it can be used from function and rule bodies\n        to evaluate rules.\n\n      rule_exists <name>\n        checks if a rule is defined\n\n      rule_list [pattern]\n        lists all loaded rules or rules matching pattern\n        Because of automatic module loading this list is only a current view for diagnostics.\n\n    Rules:\n\n      rule_show <rulename>\n        Prints the source of a rule, with the function it calls\n        - a rule which only calls the function of its name: that function is shown too\n\n      rule_edit <rulename> [-f|--function] [filepattern]\n        Opens a definition in the users editor\n        - [-f|--function]: a function definition instead of a rule\n        - [filepattern]: glob or word narrowing the files, one file opens directly\n        - only a file below the base directory opens: change into the directory of one written outside it\n\n  run - Module for running tests in resource limited environments\n\n    Variables:\n\n      NICE_LEVEL (gx)\n        The nice level 'run_test' uses\n        Default: ${NICE_LEVEL:-1}\n\n      TIMEOUT_SECS (gx)\n        Timeout for tests\n        Default: ${TIMEOUT_SECS:-300}\n\n      MEMORY_KB (gx)\n        Memory limit for tests\n        Default: ${MEMORY_KB:-33554432}\n\n  search - Rules to search text in a project\n\n    Rules:\n\n      search_for [rg_args..]\n        General searching for text, pretty paging the results.\n\n      search_issues\n        Searches for 'FIXME:|TODO:|PLANNED:' with some context around.\n\n      search_fixmes\n        Searches for 'FIXME:'.\n\n      search_todos\n        Searches for 'TODO:'.\n\n      search_planned\n        Searches 'PLANNED:'.\n\n  semver_lib - Parsing and manipulating (simplified) semantic versioning strings\n\n    For the sake of sanity not all possible semver syntax variants are\n    handled. Esp. manipulation is only implemented for the common cases. Patches welcome.\n\n    Functions:\n\n      semver_parse <semver> [major [minor [patch [prerelease [build]]]]]\n        parses semver into the provided variables\n\n      semver_validate <version>\n        Validate if string is a valid semver\n\n      semver_shortversion <version>\n        shortens a semver to the first non zero part 0.2.1-pre0+123 -> 0.2\n\n      semver_major <version>\n        shortens a semver to major\n\n      semver_majorpre1x <version>\n        shortens a semver to major with 0.x major semantic\n\n      semver_majorminor <version>\n        shortens a semver to major.minor\n\n      semver_is_patch <version>\n\n      semver_increment <version> <release|major|majorpre1x|minor|patch>\n        Increment selected part\n        'release' strips any 'prerelease' part\n        'majorpre1x' increments the minor part on a 0.x version\n\n      semver_cmp <a> <b>\n        comparing 'a' with 'b', prints 'lt', 'eq' or 'gt'\n\n      semver_lt <a> <b>\n        succeeds if a < b\n\n      semver_le <a> <b>\n        succeeds if a <= b\n\n      semver_gt <a> <b>\n        succeeds if a > b\n\n      semver_ge <a> <b>\n        succeeds if a >= b\n\n      semver_eq <a> <b>\n        succeeds if a == b\n\n  shellcheck - Support for the shellcheck shell linter\n\n    A bar module or a rule file is not plain bash: it may be written in the make/just-like\n    syntax 'preprocess' expands.  Those files are checked through 'bar_lint', which lets shellcheck\n    read the expanded text, so the sugar is no longer reported as a syntax error.  Everything else\n    is checked as it is written.\n\n    Shellcheck is surprisingly slow.  In projects using bar one can prevent the bar files\n    themselves being checked by setting in 'Barf':\n\n     SHELLCHECK_LS_FILES=\":!bar :!Bar.d :!Barf\"\n\n    Variables:\n\n      SHELLCHECK_LS_FILES (gx)\n        List of file match rules for git-ls-files.\n        Default: ${SHELLCHECK_LS_FILES:-}\n\n    Functions:\n\n      shellcheck_is_shscript <file>\n        Check if <file> is a shell script\n\n      shellcheck_is_barfile <file>\n        Check if <file> is a bar module or a rule file\n        A module is sourced from a 'Bar.d' directory and carries no shebang.  A rule file names bar\n        in its shebang ('#!/usr/bin/env bar') or is one of the names bar looks for.\n\n      bar_lint <file>\n        Lint a bar module or rule file with shellcheck\n        <file> - The module or rule file to check.\n        Shellcheck reads the text 'preprocess' prints, which is the text 'require' sources: a file\n        written in the make/just syntax is checked as the 'rule' syntax it is expanded to, and\n        only genuine shell defects are reported.  Diagnostics name the file, not the descriptor the\n        printed text was read from.  '--shell=bash' because the file's shebang names 'bar'.\n        A <file> which is not there is an error (exit 2), never a silent pass.\n\n      shellcheck_list_shscripts List all shell scripts under git control\n\n      shellcheck_lint Test if all shell scripts under git control pass the linter.\n        '--color' (without a value) forces ANSI colour even when the output is not a terminal, so\n        every captured log or memodb entry was full of escape sequences.  '--color=auto' is the\n        shellcheck default: colour for a human at a terminal, plain text for a capture.\n        A bar module or rule file goes through 'bar_lint': it is preprocessed before it is checked,\n        the make/just syntax is not bash.  Every other file is checked as it is written.\n\n    Rules:\n\n      shellcheck_has_shscripts\n        Checks if the project has any shell scripts. Must be called from a git toplevel.\n        Checks whenever any shell scripts are versioned in gitq\n\n  shellcheck_rules - Hooks shellcheck_lint into the std lint rule\n\n    Rules:\n\n      is_shellcheck_available\n        Checks if shellcheck is installed and the project has any shell scripts.\n\n  sourcetree - The source trees below a source-work root: the clones, their worktrees, their state and what\n    is not merged yet.\n\n    A source-work root holds one clone per project and every topic lives in a *worktree* below\n    '<clone>/.worktrees/<topic>'.  The main checkout of a clone is the local trunk and the single\n    place where topics meet -- a topic branch is branched off it and merged back into it, and the\n    main checkout is the only coordination point.  A topic branch that exists nowhere else is never\n    reclaimed, so 'what is not merged yet' is the question that has to be answered before anything\n    is cleaned up.\n\n    Answering it the obvious way costs a 'git worktree list', a 'git status' per tree, a merge base\n    question per tree and a shell loop to line the results up -- a dozen commands and a page of\n    output for a handful of facts.  This helper answers it in one line per tree.\n\n      bar sourcetree_state                 # the digest of '$BAR_SOURCE_ROOT'\n      bar sourcetree_state <root>          # the digest of the source-work root named here\n      bar sourcetree_state --json <root>   # the same digest as one JSON object, for a wrapper\n\n    THE ROOT\n\n    <root> is the source-work root: the argument, or '$BAR_SOURCE_ROOT' when the argument is not\n    given.  There is no default root -- with neither, the helper cannot know which layout is meant\n    and answers a usage error.  A root which is itself a repository (a '.git' at its top, or a bare\n    git directory) is reported as that one clone -- handy for a repository which is not part of a\n    source-work root -- otherwise every direct sub-directory holding a repository is one.  The walk\n    is one level deep and never descends into a clone or into object, build or vendor directories:\n    a clone belonging to a clone is not part of the layout this helper describes.\n\n    A root which is not a source-work root -- a flat index of every checkout ever made, variants of\n    one project side by side, no worktrees and no current state -- is named on stderr: a clone in\n    such a root is history, not the working tree a topic is built on.  The answer is still printed,\n    because auditing exactly that may be the intent.\n\n    THE TREES\n\n    One line per tree -- the main checkout of a clone first, then the clone's own worktrees and\n    last the trees it registered elsewhere, each group by path -- followed by the total counts:\n    the order only depends on the layout, so two digests of the same layout are comparable line\n    for line and a new tree does not reshuffle the rest.\n\n      <path>  <kind>  <branch>@<head>  <state>  <relation>  [<flags>]\n\n      <path>      the tree, relative to <root> -- '.' for the root itself -- or absolute when the\n                  tree lives outside of the root\n      <kind>      'main'  the clone's trunk, the merge base of the clone\n                  'worktree'  a worktree below '<clone>/.worktrees/'\n                  'external'  a worktree registered elsewhere; Hermes' worktree GC never\n                              reclaims an externally registered tree\n                  'bare'  a bare repository (no working tree), 'broken'  no usable git\n      <branch>    the checked out branch -- '(detached)' or '(unborn)' when it is not a branch --\n                  and '@<head>', the short commit of that tree ('-' while it has none)\n      <state>     'clean'  nothing to report\n                  'dirty <changed>/<untracked>'  files git reports as modified, staged, renamed\n                        or untracked; the two counts are kept apart because a tree full of build\n                        output is untracked while a tree with a touched tracked file is changed\n                  '?'  git refused to answer, '-'  bare\n      <relation>  for the main checkout: 'wtexclude <sources>' -- how '.worktrees/' is kept out\n                  of the checkout: 'local' ('.git/info/exclude', the convention), 'gitignore'\n                  (a *tracked* '.gitignore': the machinery put it there on first use, it is\n                  project pollution and the usual reason a main checkout reports as dirty) or\n                  'none'.  Sources are joined with '+' when both are present.\n      <relation>  for a worktree: 'unmerged <n> behind <m>' -- '<n>' commits of that tree are not\n                  reachable from the main checkout (exactly what a merge would bring in, the two\n                  halves of the merge base) and '<m>' commits of the main checkout are not in that\n                  tree; 'unmerged 0 behind 0' means 'in step with the trunk'\n      <flags>     'locked' (a stale worktree lock, with its reason), 'prunable'\n\n    The last line is the total: '(N clones, M worktrees, U unmerged, D dirty)'.\n\n    '--json' prints one object with those counts, the resolved 'root' and 'trees', one object per\n    tree: 'path' (as printed), 'abs', 'clone', 'clone_abs', 'kind', 'branch', 'commit', 'head',\n    'detached', 'state', 'changed', 'untracked', 'unmerged', 'behind', 'merge_base', 'locked',\n    'lock_reason', 'prunable', 'bare' and 'exclude' (the sources of the '.worktrees/' exclusion).\n    A number git could not answer is 'null'.  Text for reading, the object for a wrapper; both are\n    the same single pass over the same data.\n\n    EXIT CODES\n\n      0  answered -- a root without any clone answers '(0 clones, 0 worktrees, 0 unmerged, 0 dirty)'\n      2  usage error, the usage line goes to stderr and stdout stays empty\n      3  <root> is not a directory: the question could not be asked\n\n    COST (measured on this host, 2026-10-10)\n\n    A root with four clones and thirteen worktrees -- 18 lines, 2.2 kB of text or 8.3 kB of JSON --\n    takes 0.3 s (best of five; a bare 'bar rule_exists' call takes 0.09 s), the digest of a\n    throw-away root with one clone and three trees 0.15 s.  Two git calls per tree are the\n    cost ('status' and the left-right count, plus 'merge-base' when a tree is both ahead and\n    behind); the layout is read through them once, nothing is paged and no interface is entered\n    which refuses to run outside its own context.\n\n    WHAT IT DOES NOT DO\n\n    It reports, it never repairs: no fetch, no prune, no removal of a worktree, no edit of a\n    '.gitignore' and no judgement about whether a topic should live on.  Diffing two digests is\n    how a change (a merge, a new topic, a dirtied trunk) is seen.\n\n    Variables:\n\n      SOURCETREE_ROW (gA)\n        The row 'sourcetree_record' stores, filled by the collector.\n        Default: ()\n\n      SOURCETREE_ROWS (ga)\n        The rows of the clone being collected, sorted before printing.\n        Default: ()\n\n    Functions:\n\n      sourcetree_json_flag <var> <flags> <name>\n        put 'true' into <var> when the flag is set\n\n      sourcetree_exclude_json <var> <text>\n        put the exclusion sources into <var> as a JSON array\n        The sources ('local', 'gitignore') arrive joined by '+'; 'none' is no source, which is the\n        empty array and not the array of the word -- that is sourcetree's policy about its own field,\n        the array literal is 'json_list'.\n\n      sourcetree_relative <var> <root> <path>\n        put <path> as printed, relative to <root>, into <var>\n        A tree outside the root keeps its absolute path (it has no name below the root); the root\n        itself is '.', so a root which is a repository names its own main checkout.\n\n      sourcetree_pitfall <root>\n        name a root which is not a source-work root on stderr\n        A source-work root holds one clone per project and every topic in a worktree below\n        '<clone>/.worktrees/'.  A flat index of every checkout ever made -- variants of one project\n        side by side, no worktrees, and no current state -- has the clones but none of the\n        worktrees; inventorying it as if it were the working tree a topic is built on is the\n        mistake this helper exists to prevent, so such a root is named when it happens.\n\n      sourcetree_is_repo <dir>\n        it is a git repository: a '.git' entry or a bare git directory\n        A bare repository has no '.git' of its own; HEAD, objects/ and refs/ are what makes a\n        directory a git directory.  Its files are checked first only when the cheap test fails.\n\n      sourcetree_clones <root>\n        the clone directories below <root>, sorted\n        A root which is itself a repository is the single clone; otherwise every direct\n        sub-directory of one is a clone.  A project directory without a repository, a build or\n        vendor directory and a file in between are passed over.\n\n      sourcetree_exclude <clone>\n        the sources keeping '.worktrees/' out: none|local|gitignore|local+gitignore\n        The convention is a line in '.git/info/exclude': the tree is the user's business and must\n        never be committed.  A '.gitignore' carrying '.worktrees/' is the tracked variant the\n        machinery writes on first use -- reported, never repaired.\n\n      sourcetree_worktrees - 'git worktree list --porcelain' on stdin, one record per tree\n        One pass over the porcelain listing: '<path> <HEAD> <branch> <flags> <lock reason>' per\n        tree, fields separated by US (0x1f).  A path is printed as git reports it (absolute) and\n        the branch without its 'refs/heads/' prefix; the flags are 'locked', 'prunable' and 'bare'\n        in that order.  'detached' is not a flag: a tree without a branch carries an empty branch\n        field.  US and not a tab, because a tab is IFS whitespace and 'read' would collapse the\n        empty fields this carries (see 'sourcetree_record').\n\n      sourcetree_changes <tree>\n        '<changed> <untracked>' or '? ?' when git refuses\n        Counted from 'git status --porcelain': git quotes a path with an unusual name, so one\n        reported entry is one line.  Untracked entries are counted apart because a tree full of\n        build output reads as dirty while no tracked file was touched.\n\n      sourcetree_relation <clone> <main> <commit>\n        '<unmerged> <behind> <merge base>'\n        'unmerged' counts the commits of this tree which are not reachable from the main checkout:\n        the work a merge would bring in.  'behind' counts the commits of the main checkout which\n        are not in this tree: how far the trunk moved on.  Both are the two halves of the merge\n        base ('<main>...<commit>'), which is resolved only when neither half is empty.\n\n      sourcetree_record - append the row in SOURCETREE_ROW to SOURCETREE_ROWS\n        The single record format both renderers read, fields separated by US (0x1f):\n          1 abs        2 kind       3 state      4 relation   5 flags      6 branch     7 commit\n          8 changed    9 untracked 10 unmerged  11 behind    12 merge base 13 exclude  14 clone\n         15 lock reason\n        US and not a tab: a tab is IFS whitespace, so 'read' would collapse an empty 'flags' or\n        'branch' field and shift every field after it.\n        The stored row carries the sort key '<rank>:<path>' in front: 'sourcetree_clone_records'\n        sorts by it, so the main checkout comes first, then the clone's own worktrees and last the\n        trees registered elsewhere -- all by path, which keeps two digests of the same layout line\n        for line comparable.\n\n      sourcetree_clone_records <root> <clone>\n        the records of one clone, its main checkout first\n\n      sourcetree_rows_print - print SOURCETREE_ROWS in the order of their sort key\n        The rows of one clone, sorted by '<rank>:<path>' with US as the field separator: the main\n        checkout first, then the clone's own worktrees, then the trees registered elsewhere (rank\n        2), each group by path.  Nothing is sorted across clones: a clone's trees stay together.\n\n      sourcetree_collect <root>\n        one record per tree of every clone below <root>\n\n      sourcetree_counts <records>\n        '<clones> <worktrees> <unmerged> <dirty>'\n        'dirty' counts trees with local changes, not the changes themselves; 'unmerged' sums the\n        commits no main checkout has yet.\n\n      sourcetree_state_text <root> <records> <counts>\n        the aligned digest\n\n      sourcetree_state_json <root> <records> <counts>\n        the digest as one JSON object\n        One object per tree.  Every field goes through the helpers above -- the ones of this module\n        and the literals of Bar.d/json_lib -- which leave their result in a variable instead of\n        printing it: the object of one tree quotes a dozen of them and a subshell per field would\n        dominate the runtime of the machine form.\n\n      sourcetree_state [--json] [<root>]\n        the source-tree digest, see the module header\n        The body of the 'sourcetree_state' rule.  The machine form is also entered with\n        'bar --json sourcetree_state' (BAR_AGENT_JSON, see Bar.d/agent_rules).  An unknown argument\n        is a usage error, exit 2; a root which is not a directory cannot be asked, exit 3.\n\n    Rules:\n\n      sourcetree_state\n        The source-tree digest: one line per main checkout and worktree found below <root> (the\n        argument, or '$BAR_SOURCE_ROOT'), then '(N clones, M worktrees, U unmerged, D dirty)'.  See the\n        module header for the fields, the JSON shape, the exit codes and what the helper deliberately\n        does not do.\n\n  ssh - Access remote hosts\n\n    Functions:\n\n      ssh_ping [[user@]host]\n        Checks if one can connect to a remote host.\n\n      ssh_cmd [ssh_args]..\n        Executes a ssh command.\n\n      ssh_bg_cmd [ssh_args]..\n        Executes a ssh command in the background.\n\n  std_lib - Library of general functions which are always present\n\n    Functions:\n\n      memo [-c|-d] [cmd args..]..\n        memoize the result/stdout/stderr of commands, will always return the same result again\n        Memoizes for the current run only. See memodb for persistent memoization.\n        Memoized functions must be pure and only depend on their arguments and the current working directory.\n        Can not handle null bytes in stdout/stderr, use memodb when that is required.\n\n      memofn <functionnames..>\n        rewrites a function into a function that uses 'memo'\n        This renames the given functions to 'nomemo_<functionname>' and creates a wrapper\n        function with the original name that memoizes the result.\n\n      is_scalar <name>\n        Checks that a variable 'name' is a scalar (non array) variable.\n\n      called_as [pattern..]\n        Checks whenever bar is called as a symlink matching the given patterns.\n        This is usable as a rule dependency.\n\n      is_command_installed <command>\n        Checks whenever a function exists or a shell command is in PATH.\n\n      bar_now Returns the current timestamp in microseconds since epoch.\n\n      hash_args <arguments..>\n        returns the sha1hash of all supplied arguments\n\n      iset <target> [values]..\n        indirect assign values to a variable given by name, assigns to an array when more values are given.\n        When target is empty or '[*]' (missing name) then this is a no-op. Otherwise target\n        must be a declared variable When no values are given then target is cleared.\n\n  std_rules - Standard rule targets where other modules can hook in\n\n    Here we define targets which can be universally used. Other modules will hook their more\n    specific rules into these. Usually projects support only a subset of these standard targets.\n    This is fine. Any not hooked rule will only print a debug message and being a no-op otherwise.\n\n    Undesired std_rules can be renamed or deleted in the rulefile, this is preferred to\n    editing the std_rules file because it allows for easier updates of the std_rules file. All\n    these std rules just structure the targets and give debug output on the leafs. Actual\n    actions are hooked in by other modules.\n\n    Rules:\n\n      all\n        Rule that builds the software, documentation and necessary artifacts but will not do any testing.\n\n      lints\n        Run all linters.\n\n      build\n        Build libraries and executables, but not tests, docs and other non mandatory things.\n\n      tests\n        Run tests, first unit tests then integration tests.\n\n      doc\n        Build the documentation. When supported the documentation is linted as well.\n\n      bench\n        Build and run benchmarks.\n\n      agent\n        The checks an agent should run before and after it changes something.  Projects hook their\n        targets in here (the default rulefile hooks 'lints tests'), the terse output contract for a\n        human or an agent is available via 'bar check agent' / 'bar verdict agent'.\n\n      fmt\n        Reformat the source code in place.\n\n      fix\n        Apply trivial fixes, should be non breaking changes.\n\n      update\n        Update dependencies.\n\n      run\n        Run the main binary if any. This already depends on 'build' but the actual run action has\n        to be hooked into 'run' by the user in the rulefile.\n\n      audit\n        Audit for security issues.\n\n      lint_sources\n        Run a static analyzer over the source code.\n\n      lint_docs\n        Checks the documentation.\n\n      fetch_resources\n        Whenever resources are externally managed and not part of the versioned source tree they\n        should be added to this rule.\n\n      build_assets\n        Whenever assets that are not part of the normal build process have to be build, this should\n        be added to this rule.\n\n      build_libs\n        Build libraries.\n\n      build_bins\n        Build executables.\n\n      build_tests\n        Build all tests\n\n      build_unit_tests\n        Build unit tests.\n\n      build_integration_tests\n        Build integration tests.\n\n      build_docs\n        Build the documentation\n\n      build_benches\n        Build benchmarks.\n\n      build_examples\n        Build examples.\n\n      build_versioned_artifacts\n        Usually versioned files should be source and not generated by any rule. Sometimes one wants\n        exceptions from this rule. Like generating a README or CHANGELOG. This can be hooked into\n        this rule. This can be run before committing or as part of the commit check where one checks\n        for git_is_clean after running generate_versioned. When this failed then the user forgotten\n        to update the versioned files.\n\n      test_units\n        Run unit tests.\n\n      test_integrations\n        Run integration tests.\n\n      test_expensive\n        Some tests are very expensive. Since building and running makes no much difference then\n        this is accumulated in this single rule. This allows to exclude expensive tests from the\n        normal workflow rules and include them on demand.\n\n      testrelease\n        Runs the 'release_tests' in a testdir. Uses the current index (staged or last commit) This\n        is not a full release test as it wont attempt to bump version and does not generate\n        changelogs etc.\n\n      benchmark\n        Running benchmarks. This does not include building benchmarks, see the 'bench' rule.\n\n      deploy\n        Deploy the project to some server or similar.\n\n      clean\n        Clean all build artifacts, but keep assets and user configuration.\n\n      force_reset\n        Danger-Zone: reset the project into a pristine state with all non versioned data deleted or\n        reset to the original state.\n\n      activate\n        Activates maintainer curated githooks and other setup to use bar in a checked out\n        repository.\n\n      --help\n        Show help.\n\n      -h\n        Show help.\n\n      usage <name>\n        the builtin help of one rule, function or variable, without the whole documentation\n\n      --debug [rule] [args]\n        Evaluate a rule with at debug level.\n\n      --trace [rule] [args]\n        Evaluate a rule with at trace level.\n\n      --verbose [rule] [args]\n        Evaluate a rule at increased verbosity level.\n\n      --quiet [rule] [args]\n        Evaluate a rule with supressed output.\n\n  testdir - Creating an isolated directory for testing\n\n    Unlike other frameworks which do 'pre-commit' processing bar will not modify, block or\n    stash the users workdir. Instead it creates and manages directories which become populated\n    with the projects files. These testdirs also act as caches for build artifacts an can be\n    seeded from previous runs to increase build/test speed. Testdirs are automatically garbage\n    collected, only a few most recent used ones are kept for inspection and seeding new build.\n\n    Testdirs are created from git 'treeish' objects. Thus git is mandatory.\n    Testdirs are created in the git toplevel and have the name constructed from\n    '$TESTDIR_PREFIX-<timestamp>-<treehash>/'\n\n    Variables:\n\n      TESTDIR_PREFIX (gx)\n        Prefix used for testdirs.\n        Default: ${TESTDIR_PREFIX:-.test}\n\n      TESTDIR_KEEP (gx)\n        How many old testdirs.\n        Default: ${TESTDIR_KEEP:-5}\n\n      TESTDIR (gx)\n        Path to the current testdir.\n\n      TESTDIR_PREV (gx)\n        Path to the previous testdir if exits.\n\n    Functions:\n\n      is_testdir_used\n        Succeeds when a testdir is in use and current working directory is inside it.\n\n      testdir_gc Cleanup old testdirs.\n        Removed old unused testdirs. This is called automatically. It is only necessary to\n        manually call this when testdirs piled up because they where in use or when\n        'TESTDIR_KEEP' was decremented.\n\n      testdir_clean Cleanup all testdirs.\n        Removes all unused testdirs.\n\n      testdir_list Lists all testdirs.\n\n    Rules:\n\n      testdir_enter [treeish]\n        Sets up a test directory change dir into it.\n        Must be called from the git toplevel. Takes an optional treeish to construct the testdir\n        from. When no treeish is given then current index is used.  After creation the\n        'testdir_enter_hook' rule is evaluated. This rule is used to prep/populate/seed the\n        testdir. When a previous testdir exists then 'TESTDIR_PREV' point to it, the hook can use\n        that as source for populating already build artifacts.\n\n  tty_lib - Module for terminal control codes\n\n    The codes are two associative arrays with the same names: 'TTYCTL' maps a style name to the\n    escape sequence that produces it, 'TTYNIL' maps every one of those names to the empty string.\n    'TTYOUT' and 'TTYERR' are names for one of the two, decided once when the module loads:\n    'TTYCTL' when the stream is a terminal and colour was not refused, 'TTYNIL' otherwise.  A\n    caller writes '${TTYERR[fail]}text${TTYERR[reset]}' and never tests a terminal itself.\n\n    The names are styles, not colours: what 'fail' looks like is this module's decision, and a\n    project which wants a different look replaces the table instead of rewriting every message.\n\n      reset      ends a styled run\n      label      the tag of a line, 'RUN:', 'CHECK:'\n      ok         a check passed, 'OK:', 'CHECK:'\n      fail       a check failed or an unexpected problem, 'FAIL:', 'ERROR:'\n      panic      a fatal problem, 'PANIC:'\n      warn       'WARN:', 'NOTE:'\n      info       'INFO:'\n      debug      'DEBUG:'\n      trace      'TRACE:'\n      dbg        the 'DBG:' line\n      strike     struck-through text inside a 'CHECK:'\n      clear_eol  clears from the cursor to the end of the line\n      clear_line column one, then clears the line\n      cr         column one of the same line\n\n    There is no fallback for a name the table does not have: the call sites write\n    '${TTYERR[fail]}' without a ':-' and bar runs under 'set -u', so a misspelt style is an error\n    naming it, not a message which quietly lost its colour.\n\n    ADDING A STYLE\n\n    'TTYCTL' and 'TTYNIL' are writable, and 'tty_style' sets a name in both at once:\n\n      tty_style mine $'\\e[38;5;33m'     # the escape sequence itself, not a description of it\n      echo \"${TTYOUT[mine]}mine${TTYOUT[reset]}\"\n\n    The value is the sequence, the empty string goes to 'TTYNIL', so a style a project adds prints\n    nothing when the stream is not a terminal -- the same rule the built in ones follow.\n\n    THE CODES\n\n    A style is the CSI sequence '\\e[' <parameter> 'm'.  The parameters are the SGR (Select Graphic\n    Rendition) numbers below, separated by ';' when there is more than one of them:\n\n      0  reset                 1  bold              2  dim                3  italic\n      4  underline             5  blink             7  reverse            9  strike-through\n      22 neither bold nor dim  23 italic off        24 underline off\n      27 reverse off           29 strike-through off\n\n      30-37 foreground            40-47 background\n      90-97 bright foreground    100-107 bright background\n      38;5;<n> 256 colours foreground     48;5;<n> 256 colours background\n      39 default foreground        49 default background\n\n    The eight colours of 30-37 and 40-47 are black, red, green, yellow, blue, magenta, cyan and\n    white, 90-97 and 100-107 are those eight in their bright form.  'warn' is '\\e[35;1m', magenta\n    and bold, and the 'std_lib' module is the example of how the names are used.\n\n    Functions:\n\n      tty_bind - Binds the stream names and 'tty_newline' for this invocation.\n\n      tty_style <name> <sequence>\n        Defines a style, or replaces one.\n        <sequence> is the escape sequence itself, e.g. $'\\e[38;5;33m'; the codes are in the module\n        documentation above.  The name goes into 'TTYCTL' with that sequence and into 'TTYNIL' with\n        the empty string, so it disappears when the stream is not a terminal.\n\n      tty_echo [echo_args]\n        Echos only when the output is a tty.\n\n  watch_rules - Continious supervision by a file watcher\n\n    Rules:\n\n      watch [rule [arguments..]]\n        Evaluates a rule or MAIN whenever a versioned files changed in the current directory.\n        File watchers hook into this rule.\n\n      watch_git_index [rule [arguments..]]\n        Evaluates a rule or MAIN whenever a the git index changed.\n        File watchers hook into this rule.\n\n\nMAIN API\n\n  bar - The main BAsh Rulez script\n\n    Provides only basic functionality for module loading and debugging and starting rule\n    evaluation. Loads the 'std_lib' and 'rule_lib' to make them omnipresent.\n\n    Variables:\n\n      BAR_STATE_INIT_FNS (ga)\n        Functions a module wants re-run after a dump was sourced.\n        Default: ()\n\n      BAR_STATE_DUMP (gx)\n        Dump file, 'off', or empty for the default path.\n        Default: ${BAR_STATE_DUMP:-}\n\n      BAR_STATE_MAGIC (gr)\n        First line of a dump: its format and version.\n        Default: # bar-state 1\n\n    Functions:\n\n      bar_main [rulefile] [--bare] [rule [arguments..]]\n        Loads a rulefile and executes rule with optional arguments\n        [rulefile]           - File to load the user rules from.\n                               Default: Barf|barf|.Barf|.barf when called as bar or symlink.\n                                        Makeitsof|makeitsof|.Makeitsof|.makeitsof|\\$HOME/.Makeitsof when called as makeitso.\n        [--bare]             - Don't eval SETUP, PREPROCESS, POSTPROCESS and CLEANUP.\n        [rule [arguments..]] - Target rule with arguments.\n                               Default: MAIN\n\n      bar_installation_bar_d - the 'Bar.d' of the installation 'bar_installation' names\n\n      bar_installation_barf - the shipped default Barf of that installation\n\n      bar_origin_dir - the 'Bar.d' of the installation bar was run from, when it is not the one in use\n        A project may carry its own (vendored) 'Bar.d', then the installation bar was run from is\n        where modules missing from the project are taken from.  When bar runs from its\n        installation there is no origin.  'makeitso' has no vendored copy either: its modules live\n        in a single, absolute directory.\n\n      bar_vendor_modules <modpath>\n        installs a module of Bar.d which is missing from the origin, prints what it installed\n        <modpath> - The path the module would have in 'Bar.d' (a glob is expanded there).\n        Copies the module(s) from the origin installation into 'Bar.d'.  An existing module is\n        never touched (that is what '--update' of the init rules is for) and the origin is never\n        written to, installing only ever adds a copy here.  Prints the installed paths, returns\n        non-zero when there is no origin, the name is not a module of this 'Bar.d' or the origin\n        does not have it either.\n        Every job the scheduler starts calls this before it evaluates anything.  The parent's\n        channel is not shared: two readers on one connection tear frames (measured), the parent's\n        stop needs every copy of the write end closed, and no job may talk to a filter it does not\n        own.  Dropping it here costs nothing -- a job which never preprocesses never starts one --\n        and 'barf_pp_ensure' starts the job's own on its first 'require', in the shell whose\n        descriptors every module of that load inherits.\n        Closing is the whole of the stop: awk sees EOF on the frame pipe, runs its 'END' and exits.\n        Nothing waits for it here, and the filter is disowned (:func:`barf_pp_start`), so there is\n        no job of this shell to wait for either -- a 'wait' on the pid answers 127 at once, and this\n        runs from the EXIT trap, where a failing command under errexit ends the run with that status\n        instead of the one the rules earned.\n\n      preprocess <file>\n        Print a rule file with its make/just syntax expanded\n        <file> - The rule file to print.\n        Prints the text which 'require' sources for the file: a line written in the syntax close to\n        makefiles and justfiles is expanded into the 'rule' syntax and every other line is passed\n        through unchanged.  The preprocessor is infallible, it never refuses input -- what it does\n        not understand is reported by bar's own parser, at the line it is on.  A file it cannot read\n        is an error rather than an empty expansion.  'bar preprocess <file>' shows exactly the text\n        which gets sourced.  That syntax is documented under \"RULE FILE PREPROCESSOR\" in 'bar help'.\n        The path itself when it does not name one of our descriptors.\n\n      require [--opt] [modules..]\n        Loads modules.\n        [--opt]     - Make the modules optional, when they do not exist then no warning is emitted\n                      and no failure returned.\n        [modules..] - List of module names to be loaded. Modules are loaded only once, further\n                      attempts to load it will be a no-op.  A name may be a glob which expands\n                      to several modules.\n        A module which is missing from the 'Bar.d' in use is taken from the installation bar was\n        run from (see :func:`bar_vendor_modules`) and loaded from there.  When there is no origin\n        or the module does not exist there either it is missing: with '--opt' that is silent and\n        succeeds, without it a warning is emitted and a failure returned.\n\n      bar_state_off [value]\n        True when the value is one which turns the dump off.\n        <value> - Default: 'BAR_STATE_DUMP'.\n\n      bar_state_path - Sets 'BAR_STATE_PATH' to the dump file of this invocation.\n        'BAR_STATE_DUMP' when the environment set it, else '.bar.<rulefile name>.dump' beside the\n        rulefile.  The name carries the rulefile, so two rulefiles in one directory never share a\n        dump and nothing needs to know whether the run is 'bar' or 'makeitso', in a project or next to\n        an installation: the rulefile is what the state of a run depends on, and where it is, is\n        where the dump goes.  Empty when there is no rulefile to name it after.  The setting is the\n        one the environment had: the loader has to know where the dump is before it can read a\n        rulefile which might name another.\n\n      bar_state_volatile <name>\n        True for a variable or function a dump must not carry.\n        bar's own state, and what belongs to the invocation rather than to the loaded modules.\n\n      bar_state_snapshot - Records the declarations the module loads are about to change.\n        Taken before the first module is sourced: whatever a module leaves the same came from the\n        environment and must not be written into the dump.\n\n      bar_state_sums <files..>\n        Prints one checksum over the paths and the file contents.\n        The paths are part of it, so the checksum answers for the set of inputs as well as for each\n        of them.\n\n      bar_state_dump - Writes the state of the loaded modules to the dump file.\n\n      bar_state_load - Sources the dump instead of the modules, when it is current.\n        Succeeds only when a dump exists and is current, so that the caller can leave the files\n        alone; any other case is a miss and it loads them as always.\n        A module is sourced from the text the filter printed, so its frame names '/dev/fd/NN'; the\n        descriptor is mapped back to the module.  Line numbers need no such help: the filter prints\n        one line per line.\n\n      bar_report_condense - True when the source location should be condensed on the console.\n        The verbose 'file:line:func:' location is only condensed when the output goes to the\n        console. Redirected output keeps the full, easily filterable format.\n\n      bar_report_loc [N]\n        Sets BAR_REPORT_LOC for a report N frames up the stack.\n        Like 'source_info N' but condenses the source location of consecutive reports from the\n        same file and function: the location is printed once and then replaced by blanks of the\n        same width. Sets 'BAR_REPORT_REPEATED' when this report continued such a block.\n\n      bar_report_condensed - True when the repeated location is to be blanked down to a ':'.\n        'BAR_REPORT_STYLE=tag' (default) keeps the message tag in front of the blanked location,\n        'colon' blanks the tag as well so that only the ':' of the location remains.\n\n      DBG [message..]\n        For print-style debugging, should not be present in production code.\n\n      die [message..]\n        Prints 'message' to stderr and exits with failure\n        Unexpected fatal problem. Will terminate execution, CLEANUP rules will be called.\n        The source location is condensed like 'error' does, see there for 'BAR_REPORT_CONDENSE' and\n        'BAR_REPORT_STYLE'.\n\n      error [message..]\n        Print a error message to stderr.\n        Unexpected but not fatal.\n        The source location is printed for the first report of a function and condensed to blanks\n        for the following ones, so a multi line report stays readable. 'BAR_REPORT_CONDENSE' is\n        'always', 'never' or 'auto' (default: only on a console, redirected output keeps the full\n        format) and 'BAR_REPORT_STYLE' is 'tag' (default) or 'colon'.\n\n      warn [message..]\n        Print an warning to stderr.\n        Expected, non fatal problem.\n\n      note [message..]\n        Print an important notice to stderr.\n        Important message the user should be notified about.\n\n      info [message..]\n        Print an informal message to stderr.\n        Broader message about non rule related progress.\n\n      debug [message..]\n        Print a debug message to stderr.\n        Finer grained progress messages.\n\n      trace [message]\n        Prints a trace message to stderr.\n        Very verbose function call or finer messages.\n\n\nDOCUMENTATION WRITING GUIDELINES\n\n  Bar uses a structured documentation format that serves both human readers and automated\n  tools like help generation and bash completion. Documentation uses '##' comment markers.\n\n  File-Level Documentation:\n\n    Use '###' at the start of a file to describe the module/file:\n\n     ### This module provides cargo/rust support.\n\n  Variable Documentation:\n\n    Variables declared with 'declare' can be documented on the same line:\n\n     declare -g MYVAR=\"value\" ## Description of the variable\n\n  Function Documentation:\n\n    Functions should have documentation in the form:\n\n     function name ## [--opt] <required> [optional..] - Short description\n\n    The parameter list uses a formal syntax:\n    - <param>    - Mandatory parameter (prototype)\n    - [param]    - Optional parameter (prototype)\n    - <param..>  - One or more occurrences (suffix .. after closing >)\n    - <param>..  - One or more occurrences (suffix .. after closing >)\n    - [param..]  - Zero or more occurrences (suffix .. after closing ])\n    - [param]..  - Zero or more occurrences (suffix .. after closing ])\n    - <> and []  - Can be nested\n    - word       - Anything not in <> or [] is a literal (like 'as', 'into', ':')\n    - --flag     - Literal flag (can be in <--flag> for prototype completion)\n    - -f         - Literal short flag\n    - a|b        - Alternatives where a and b can be any of the above\n                   (a|b|c|d etc. any number allowed)\n                   Note: <a..|b> is at least one 'a' or a single 'b'\n                         <a|b>.. is at least one of 'a' or 'b'\n    - param      - Parameter names serve as prototypes for completion\n\n    Examples:\n\n    - function rename ## <this> as|into <that> - rename this as/into that\n      Requires literal 'as' or 'into' at the 2nd position.\n    - function process ## <input:> <output> - process input to output\n      The colon ':' after <input> is a literal character.\n\n    Parameter identifiers start with an alphabetic character followed by [[:alnum:]-_].\n    These serve as prototypes for completion and are refined in following documentation.\n\n    Multi-line documentation continues on following lines starting with '##':\n\n     function foo ## [--verbose|-v] <input> [output] - Process files\n     {\n         ## [--verbose|-v] - Enable verbose output\n         ## <input>         - Input file to process\n         ## [output]        - Optional output file (defaults to stdout)\n         ...\n     }\n\n  Rule Documentation:\n\n    Rules are documented with '##' lines before the rule definition:\n\n     ## <target> - Build the specified target\n     rule build:\n\n    For rules with parameters, use the same formal syntax as functions:\n\n     ## [--toolchain] <target> [options..] - Build target\n     rule build:\n\n  Documentation Prototypes for Completion:\n\n    Parameter identifiers map to completion functions. Common prototypes include:\n\n    - <file>      - Any file on filesystem\n    - <directory> - Any directory\n    - <path>      - Any valid path\n    - <text>      - Any text input\n    - <number>    - Numeric input\n    - <rule>      - Existing rule name\n    - <command>   - Command or function name\n\n    Module-specific prototypes can be defined (e.g., <toolchain> in cargo module).\n    See contrib/bar_complete for completion implementation details.\n    \n  Defining Custom Completion Prototypes:\n\n    Modules can define custom prototypes for parameter completion using single-hash comments\n    that must start at column 0 (no leading whitespace):\n    \n      # prototype: \"mytype\" = \"file\"\n      # prototype: \"myfile\" = \"file existing local\"\n      # prototype: \"mycommand\" = \"command\"\n      # prototype: \"toolchain\" = \"extcomp cargo\"\n      # prototype: \"gitargs\" = \"extcomp git\"\n    \n    These definitions register a prototype with a completer. The format is:\n\n      # prototype: \"prototype_name\" = \"completer_spec\"\n    \n    Where completer_spec is either:\n    - A built-in completer name (file, directory, path, rule, command, etc.)\n    - A completer with predicates (e.g., \"file existing local\")\n    - \"ext function_name\" for external/custom completers (calls bar --bare function_name)\n    - \"extcomp command\" for black-box external command completion (invokes command's native bash completion)\n    \n    The \"extcomp\" type enables leveraging any command's existing bash completion by invoking\n    it as a black box. For example, \"extcomp git\" will use git's native completion for all\n    git subcommands and flags. This works with git, cargo, ssh, and any other command that\n    has bash completion support.\n    \n    Note: The '# prototype:' comment must start at the beginning of the line (column 0).\n    \n    These are registered as \"module@prototype\" in the completion registry, allowing\n    module-specific completion behavior while maintaining fallback to global prototypes.\n\n\nBASH COMPLETION\n\n  Bar supports bash completion for rulefiles, rules, functions, and their arguments. Completion\n  is automatically installed when you run 'bar init_install'.\n\n  The completion script is installed to '~/.bash_completion/bar_complete'. To enable it,\n  either source it in your .bashrc:\n\n   source ~/.bash_completion/bar_complete\n\n  Or if you have bash-completion package installed, it should be automatically loaded.\n\n  Completion works for:\n\n  - bar <TAB>       - completes rulefiles, rules, and functions in current directory\n  - makeitso <TAB>    - completes rulefiles, rules, and functions\n  - ./bar <TAB>     - completes rulefiles, rules, and functions from local bar\n\n  The completion logic will:\n\n  1.  Find any user rule files in current directory (files containing 'function' or 'rule',\n      or with shebang pointing to bar/makeitso)\n  2.  Complete available bash functions and rules from default rule file and Bar.d/ modules\n  3.  Provide context-aware argument completion based on parameter types\n\n\nRELEASES\n\n  There will be no versioned releases, we only maintain a rolling 'main' branch (and plenty\n  feature and development branches). This is not a problem since we promote bar to be vendored\n  into projects using it. Thus you can freely update/edit/replace any part of it.  The\n  'git_search' function helps to track the history of files.\n\n\nCONTRIBUTIONS\n\n  bar uses radicle (https://radicle.dev/). Issues and patches are maintained on radicle. Its\n  rid is rad:z3WhBdHt1VVNyeQ61zpY66pNzuSrP. There is a mirror on github that is lazily\n  updated. PR's and Issues opened on github may be ignored or handled with a delay.\n\n\nSUPPORT AND CONTACT\n\n  Radicle Explorer (readonly):\n  https://seed.pipapo.org/nodes/seed.pipapo.org/rad:z3WhBdHt1VVNyeQ61zpY66pNzuSrP\n\n  Public channels:\n  Mastodon: @cehteh@social.tchncs.de use the #BAshRulez hashtag\n  IRC: ping cehteh on irc.libera.chat or irc.oftc.net\n\n  Email for contracted work, payed support:\n  ct.bar@pipapo.org\n\n\nLICENSE\n\n    bar -- BAsh Rulez\n    Copyright (C) 2025  Christian Thäter <ct.bar@pipapo.org>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as\n    published by the Free Software Foundation, either version 3 of the\n    License, or (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n","path":"README","lastCommit":{"id":"8454e03c6c5359f5dd97d0ae46cc06617c69add2","author":{"name":"ct_vibes","email":"ct_vibes@z6MkexgBnxseYVTf8omTGHytLSS5xCxno4xQ7zrMFfC6fuiH"},"summary":"bar: a source tree initializes a project, and a git repository is a project boundary","description":"'<checkout>/bar init' inside a git repository could neither find what to install nor stay out of\nthe project above it: the origin came from 'command -v bar' (nothing without an installed bar, so\nthe run died), and the rule-directory walk went past a git repository which carried no rule\ndirectory, so a fixture created below another project's checkout took that project's rulefile and\nstate for its own.\n\nWhat is installed now comes from 'bar_installation': the installed prefix when bar runs from one,\nelse the tree the running bar lives in ('<root>/bar' beside '<root>/Bar.d').  The walk ends at a\ngit repository which carries no rule directory, unless BAR_CROSS_REPO is set, and at such a root\nthe modules are taken from that installation.  An inherited BAR_DIR no longer answers for the tree\nbar runs in -- it is exported, and a child of a running bar inherits it.\n\nReproducers: tests/test_vendoring.sh AC11 (a source tree initializes a project without being\ninstalled) and AC12 (a nested repository takes no rulefile from the project above it).","parents":["d6272086aff4e8669dcaf89a8e67d4a1271692c2"],"committer":{"name":"ct_vibes","email":"ct_vibes@z6MkexgBnxseYVTf8omTGHytLSS5xCxno4xQ7zrMFfC6fuiH","time":1793650191}}}