#!/bin/sh
#
# Write the commit back onto every item it cites — item SR#51, the return journey.
#
# `git log --grep` answers "which commit mentioned SR#46" only for somebody standing in a
# checkout. This answers it from the instance, which is where the work is planned and where
# anybody asking about an item is already looking. Neither direction was answerable before.
#
# **It can never fail a commit**, and that is structural rather than careful: git ignores
# this hook's exit code, and by the time it runs the commit is already made. Everything
# below is best-effort and says so when it gives up.
#
# Installed with the `commit-msg` hook beside it:
#
#     git config core.hooksPath hooks

set -u

short=$(git rev-parse --short HEAD)
subject=$(git log -1 --pretty=%s)
body=$(git log -1 --pretty=%B | grep -v '^#' || true)

refs=$(printf '%s' "$body" | grep -oE 'SR#[1-9][0-9]*' | sed 's/^SR#//' | sort -u || true)

# **Read out of the message rather than guessed at.** `scripts/check.py` writes its counts into
# the commit message by convention — "Gate: 5,610 passed, 41 skipped" — and this hook has no
# other way to know a gate ran at all. An absent line means no record, which is the honest
# answer: a verification nobody ran is worse than none, because the table's whole value is that
# it says what it is.
gate=$(printf '%s' "$body" | grep -oE '^Gate: .*' | head -1 | sed 's/^Gate: //' || true)

# The tree the commit names, which is what a record expires against. Empty outside a checkout,
# and the record is kept without it — it simply cannot expire.
tree=$(git rev-parse 'HEAD^{tree}' 2>/dev/null || true)

if [ -z "$refs" ]; then
	exit 0
fi

if ! subroutine whoami >/dev/null 2>&1; then
	printf '%s\n' \
		"The instance could not be reached, so $short was not recorded against the items it" \
		"cites. Nothing is lost — 'git log --grep SR#' still finds it." >&2
	exit 0
fi

# **An amend replaces a commit, so the record has to replace too.** This hook fires again on
# `git commit --amend`, and the sha it wrote a moment ago has stopped existing — so leaving it
# would put "Committed as 34d87d3" on an item where no such commit can be found, which is
# worse than saying nothing. The reflog is what names an amend; git tells a hook nothing.
replaced=""

case "$(git reflog -1 --format='%gs' 2>/dev/null)" in
	"commit (amend):"*)
		replaced=$(git rev-parse --short 'HEAD@{1}' 2>/dev/null || true)
		;;
esac

for ref in $refs; do
	# Read once and asked twice: whether this commit is already written down, and whether a
	# gate is the kind of evidence this item can carry.
	record=$(subroutine show "$ref" --json 2>/dev/null || true)

	# Asked before writing, so re-running this by hand cannot say the same thing twice.
	if printf '%s\n' "$record" | grep -q "Committed as $short"; then
		continue
	fi

	if [ -n "$replaced" ]; then
		# Deleted rather than edited, because that is the only thing this product will do to
		# a comment — a comment is attributed prose, and rewriting it under somebody's name
		# is deliberately not possible. Failure here is ignored: an extra line in the record
		# is a far smaller problem than a hook that stops half way.
		subroutine uncomment "$ref" "Committed as $replaced" >/dev/null 2>&1 || true
	fi

	if ! subroutine comment "$ref" "Committed as $short — $subject" >/dev/null 2>&1; then
		printf '%s\n' "Could not record $short against SR#$ref." >&2
	fi

	# **What was checked, recorded against the tree it was checked on** — item SR#1121, and it
	# is here rather than behind a command an agent must remember because of a measurement:
	# *attach it to work already happening* is the highest-adoption lever on this instance at
	# 93%, and *enforcement* the lowest at +0.24pp. This hook is the 93%. It costs the writer
	# nothing and starts at the adoption rate the commit itself has.
	#
	# **The tree, not the commit, is what makes it expire.** A gate is run before the commit
	# it is about — SR#894 — so a sha names something that did not exist when the checks ran,
	# where a tree names the content either way. Both are sent; only the tree decides staleness.
	#
	# **Only where the commit message says a gate ran.** Nothing here can know whether one
	# did, and inventing a record would be worse than having none: the whole value of this
	# table is that it is honest about what it is, which is a record and never a proof.
	#
	# **And only against the work the commit did** — item SR#1153, found by driving this hook the
	# commit after it shipped. A message cites refs in two roles: what it implements, and what it
	# reasons from. Commenting on both is right and is SR#51 — *this commit mentioned you* is true
	# of a citation, and `uncomment` takes it back. Recording a **gate** on both is not: it puts
	# fresh evidence on finished work the run says nothing about, and a verification cannot be
	# deleted on any surface, so every one of them is permanent.
	#
	# Two conditions, and neither needs a convention anybody has to remember:
	#
	# - **A task.** A gate does not verify a decision document, and `subroutine verify` refuses
	#   one — which used to be reported here as a failure, on an ordinary correct message.
	# - **Not already finished.** Work a commit is *doing* is open or in progress when this
	#   fires, because the order is claim, start, gate, commit, then done.
	#
	# **What it still cannot tell** is an *open* item cited only for its reasoning. Rarer, and
	# the honest failure is the same one an absent `Gate:` line already has.
	#
	# Whole-line matches, because both keys appear again inside every linked item this output
	# carries and a loose grep would read a blocker's category as this one's.
	# `test_the_shape_a_commit_hook_reads_is_the_shape_it_greps_for` drives the real command.
	implements=""

	if printf '%s\n' "$record" | grep -qxE '  "entity_type": "task",?'; then
		if ! printf '%s\n' "$record" | grep -qxE '    "status_category": "(done|cancelled)",?'; then
			implements="yes"
		fi
	fi

	if [ -n "$gate" ] && [ -n "$implements" ]; then
		if ! subroutine verify "$ref" --summary "$gate" --tree "$tree" --commit "$(git rev-parse HEAD)" >/dev/null 2>&1; then
			printf '%s\n' "Could not record the gate against SR#$ref." >&2
		fi
	fi
done
