← Files NightshiftARCHIVED FILE

runtime/morning-receipt-emit.jq

4.8 KB · Oct 2, 2026 · 00:30 UTC

↓ Download file

# The JSON half of morning-receipt.sh: lift printable values out of the documents the receipt
# renders, and nothing else.
#
# morning-receipt.sh owns every decision — which sections a view carries, what each line says,
# what an absent value renders as, what order rows come in. This program never decides anything;
# it reads. Four operations share one argument set so a single shell wrapper can bind it,
# selected by $op:
#
#   recs      the ledger as raw text. One group per non-empty line: kind r or u, the $fields
#             cells, and the line's index, $FS between fields and $RS after the group. A line
#             that is not a JSON object comes back as kind u with empty cells, so a record's
#             index still lines up with its details.
#   details   the ledger as raw text. <index> $FS <key> $FS <value> $RS for every key of a
#             record's details object, keys in byte order. A domain's extra fields live there,
#             so they travel as opaque pairs the shell prints without knowing their names.
#   policy    one accepted shift-policy document. The shell classifies the file first
#             (absent / malformed / accepted) and only asks for fields when the document
#             validates. One h group — shiftId, createdAt, source,
#             verificationLevel, toolingPolicy, completionMode, the selectedDebt ids joined by
#             ", " — then one a group per allowance in document order: category, scope,
#             provenance.
#   compare   one comparison document. <ids> $FS <sources> $FS <status> $FS <locator> $RS per
#             row, ids and sources joined by ", ", taking the first key present of each alias
#             set and skipping a row that is not an object.
#
# Every payload is control-scrubbed before it enters the stream, so a group carries exactly the
# cells it declares and never a separator of its own.

def scrub: gsub("[\\x00-\\x1f\\x7f]"; " ");

def canonjson:
  if type == "object"
  then "{" + ([to_entries | sort_by(.key) | .[] | (.key | tojson) + ":" + (.value | canonjson)]
              | join(",")) + "}"
  elif type == "array" then "[" + ([.[] | canonjson] | join(",")) + "]"
  else tojson
  end;

def cell:
  (if . == null then ""
   elif type == "string" then .
   elif type == "boolean" or type == "number" then tojson
   else canonjson
   end)
  | scrub;

def names($s): $s | split("\n") | map(select(length > 0));

def field($k): if type == "object" then .[$k] else null end;

def firstof($keys): . as $r
  | [$keys[] | . as $k | $r | field($k) | select(. != null)]
  | (.[0] // null);

def listcell: if type == "array" then ([.[] | cell] | join(", ")) else cell end;

def payload: split("\n") | map(select(length > 0));

def parse: try fromjson catch null;

def recs($fields; $FS; $RS):
  payload
  | to_entries[]
  | .key as $i
  | (.value | parse) as $r
  | if ($r | type) == "object"
    then "r" + $FS + ([$fields[] | . as $k | $r | field($k) | cell] | map(. + $FS) | join(""))
      + ($i | tostring) + $RS
    else "u" + $FS + ([$fields[] | ""] | map(. + $FS) | join("")) + ($i | tostring) + $RS
    end;

def details($FS; $RS):
  payload
  | to_entries[]
  | .key as $i
  | (.value | parse | field("details")) as $d
  | if ($d | type) == "object"
    then ($d | to_entries | sort_by(.key) | .[]
      | ($i | tostring) + $FS + (.key | scrub) + $FS + (.value | cell) + $RS)
    else empty
    end;

def policy($FS; $RS):
  (if type == "object" then . else {} end) as $P
  | ("h" + $FS
      + ([["shiftId", "createdAt", "source", "verificationLevel", "toolingPolicy",
           "completionMode"][] | . as $k | $P | field($k) | cell]
         | map(. + $FS) | join(""))
      + ([$P | field("selectedDebt") | if type == "array" then .[] else empty end | cell]
         | join(", "))
      + $RS),
    ($P | field("allowances")
      | if type == "array" then .[] else empty end
      | select(type == "object")
      | "a" + $FS + (field("category") | cell) + $FS + (field("scope") | cell)
        + $FS + (field("provenance") | cell) + $RS);

def compare($FS; $RS):
  . as $doc
  | (if ($doc | type) == "array" then $doc
     elif ($doc | type) == "object"
     then (($doc | firstof(["rows", "findings", "comparison", "entries"])) as $r
       | if ($r | type) == "array" then $r else [] end)
     else []
     end)
  | .[]
  | select(type == "object")
  | (firstof(["id", "finding", "recordId"]) | cell) as $id
  | (firstof(["class", "classification", "state"]) | cell) as $class
  | (firstof(["digest"]) | cell) as $digest
  | (firstof(["sources", "sourceClass", "source"]) | listcell) as $sources
  | (firstof(["locator", "at"]) | cell) as $locator
  | $id + $FS + $class + $FS + $digest + $FS + $sources + $FS + $locator + $RS;

  if $op == "recs" then recs(names($fields); $FS; $RS)
elif $op == "details" then details($FS; $RS)
elif $op == "policy" then policy($FS; $RS)
else compare($FS; $RS)
end

SHA-256: b27ce3ec2b2436d9936050e6587175b7afdb2b07b99ed22b527cf0da351db40f