Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/decisions/ADR-008-profile-c-domains.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"domains":[{"domain":"echidnabot-proof-obligation","name":"JCS (RFC 8785) of the JSON object {claim, context, prover, repo} of a proof obligation","purpose":"ProofObligationRecord.id in echidnabot: resubmitting the same obligation gives the same id","repos":["hyperpolymath/echidnabot"]},{"domain":"gv-clade-index","name":"github.com/<owner>/<name> of a repository","purpose":"clade identifiers (ADR-008 amendment D320)","repos":["hyperpolymath/gv-clade-index"]},{"domain":"proof-goal","name":"JCS (RFC 8785) of the goal text as a JSON string, exact text, not trimmed or normalised","purpose":"the cross-tool content address of a proof goal: one goal has one id in every tool that names it","repos":["hyperpolymath/echidnabot","hyperpolymath/proof-burrower"]},{"domain":"prove-result","name":"JCS (RFC 8785) of an ECHIDNA prove result as serialised by the consumer","purpose":"ProofReceipt.result_id in proof-burrower: identical prove results share an id","repos":["hyperpolymath/proof-burrower"]}],"schema":"estate-uuid-v8-domains/1"}
40 changes: 40 additions & 0 deletions docs/decisions/ADR-008-uuid-v8-estate-standard.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,46 @@ shared profile T and C generators are the next standards increment. The
gv-clade-index derivation change follows it. Each repository then migrates
under P3 as before; strict only means a repository is red until it does.

== Amendment: the profile C domain register (owner answer, 2026-10-14)

Profile C requires a _registered_ domain, but there was no register. Asked
where domains are registered, the owner chose one register in this
repository over per-repository declarations in each chora deed. Per-repository
declarations can only check uniqueness within one repository.

*The register* is `docs/decisions/ADR-008-profile-c-domains.json`. It is
I-JSON in JCS form (JSON-POLICY, D306), and each entry is
`{domain, name, purpose, repos}`:

* `name` says in words what is hashed after `domain:`.
* `repos` lists every repository that mints ids in that domain.

A domain shared by several repositories is a single registration. For
example, `proof-goal` is the cross-tool content address of a proof goal,
which echidnabot and proof-burrower compute the same way, so one goal has one
id in both. Within that one registration the domain is still used for one
purpose only, as ADR-008 requires.

*The check* is `scripts/check-uuid-v8-domains.sh [--registry FILE]
[DOMAIN...]`. It validates the register's bytes (one line, printable ASCII,
JCS, one final LF), the entry shape, the domain rule and strict ordering, so
no domain is registered twice. Given DOMAIN arguments, it also fails on any
that is not registered. A repository that mints profile C ids runs it in CI
against a pinned standards checkout with the domains it uses.
`scripts/tests/check-uuid-v8-domains-test.sh` plants a defect for each rule and
expects each one to fail, for its own reason.

*First entries.* The domains of the P3 pilot repositories are registered
from their current code:

* `echidnabot-proof-obligation`;
* `proof-goal`, shared by echidnabot and proof-burrower;
* `prove-result`.

`gv-clade-index` is registered as ruled in the D320 amendment.
echidna has no content-id call site yet, so it registers none.
`berrywiki-import` is left for berrywiki to register with its own name form.

== What this record does not do

It does not migrate any repository (P3), change the deed grammar, or update
Expand Down
102 changes: 102 additions & 0 deletions scripts/check-uuid-v8-domains.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: MPL-2.0
#
# check-uuid-v8-domains.sh — the ADR-008 profile C domain register.
#
# Validates docs/decisions/ADR-008-profile-c-domains.json and, given DOMAIN
# arguments, checks that each one is registered. A repository that mints
# profile C ids runs it in CI with the domains it uses, against a pinned
# standards checkout, so an unregistered domain fails there.
#
# Usage: check-uuid-v8-domains.sh [--registry FILE] [DOMAIN...]
#
# Exit contract:
# 0 = the register is valid, and every DOMAIN given is registered
# 1 = the register is invalid, or a DOMAIN is not registered (each is named)
# 2 = the check could not run (jq missing, register missing or unparseable)
#
# What "valid" means:
# * One line of printable ASCII and exactly one final LF. Within that shape
# (objects, arrays and strings only, ASCII keys) `jq -cS .` is the JCS
# (RFC 8785) form, so the bytes must equal it (JSON-POLICY, D306).
# * Top level is exactly {domains, schema}, schema "estate-uuid-v8-domains/1".
# * Every entry is exactly {domain, name, purpose, repos}:
# domain non-empty printable ASCII (0x21..0x7e) without ":" (ADR-008);
# name what is hashed after "domain:", in words;
# purpose what the ids identify;
# repos the owner/repo list that mints ids in this domain, sorted,
# unique. A domain shared by several repositories is one
# registration (for example a cross-tool content address).
# * Domains are strictly increasing, so no domain is registered twice.
set -uo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
REGISTRY="$ROOT/docs/decisions/ADR-008-profile-c-domains.json"
if [ "${1:-}" = "--registry" ]; then

Check failure on line 35 in scripts/check-uuid-v8-domains.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5sfGi8KAjJag5XWJu&open=AaE5sfGi8KAjJag5XWJu&pullRequest=1246
[ $# -ge 2 ] || { echo "check-uuid-v8-domains: --registry needs a file" >&2; exit 2; }

Check failure on line 36 in scripts/check-uuid-v8-domains.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5sfGi8KAjJag5XWJv&open=AaE5sfGi8KAjJag5XWJv&pullRequest=1246
REGISTRY="$2"
shift 2
fi

command -v jq >/dev/null 2>&1 || { echo "check-uuid-v8-domains: jq is not on PATH" >&2; exit 2; }
[ -f "$REGISTRY" ] || { echo "check-uuid-v8-domains: no register at $REGISTRY" >&2; exit 2; }

Check failure on line 42 in scripts/check-uuid-v8-domains.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5sfGi8KAjJag5XWJw&open=AaE5sfGi8KAjJag5XWJw&pullRequest=1246
jq -e . "$REGISTRY" >/dev/null 2>&1 || {
echo "check-uuid-v8-domains: $REGISTRY does not parse as JSON" >&2; exit 2;
}

errors=()

# Shape of the bytes: one line, printable ASCII, one final LF, JCS order.
if [ "$(tail -c 1 "$REGISTRY" | od -An -c | tr -d ' ')" != '\n' ] || [ "$(wc -l < "$REGISTRY")" -ne 1 ]; then

Check failure on line 50 in scripts/check-uuid-v8-domains.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5sfGi8KAjJag5XWJx&open=AaE5sfGi8KAjJag5XWJx&pullRequest=1246

Check failure on line 50 in scripts/check-uuid-v8-domains.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5sfGi8KAjJag5XWJy&open=AaE5sfGi8KAjJag5XWJy&pullRequest=1246
errors+=("the register must be exactly one line ending in one LF")
fi
if LC_ALL=C grep -q '[^ -~]' "$REGISTRY"; then
errors+=("the register must be printable ASCII")
fi
if ! cmp -s <(jq -cS . "$REGISTRY") "$REGISTRY"; then
errors+=("the register is not in JCS form (jq -cS . | cmp)")
fi

# Structure and content.
while IFS= read -r e; do
[ -n "$e" ] && errors+=("$e")

Check failure on line 62 in scripts/check-uuid-v8-domains.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5sfGi8KAjJag5XWJz&open=AaE5sfGi8KAjJag5XWJz&pullRequest=1246
done < <(jq -r '
def domain_ok: type == "string" and test("^[!-9;-~]+$");
def repo_ok: type == "string" and test("^[A-Za-z0-9-]+/[A-Za-z0-9._-]+$");
def text_ok: type == "string" and length > 0;
if type != "object" then "the register is not an object"
elif keys != ["domains", "schema"] then "top-level keys must be exactly domains, schema"
elif .schema != "estate-uuid-v8-domains/1" then "schema must be \"estate-uuid-v8-domains/1\""
elif (.domains | type) != "array" or (.domains | length) == 0 then "domains must be a non-empty array"
else
(.domains | to_entries[] | .key as $i | .value as $e |
if ($e | type) != "object" then "domains[\($i)] is not an object"
elif ($e | keys) != ["domain", "name", "purpose", "repos"] then
"domains[\($i)]: keys must be exactly domain, name, purpose, repos"
else
(if ($e.domain | domain_ok) then empty else
"domains[\($i)]: invalid domain \($e.domain | tojson) (non-empty printable ASCII, no \":\")" end),
(if ($e.name | text_ok) then empty else "domains[\($i)]: name must be a non-empty string" end),
(if ($e.purpose | text_ok) then empty else "domains[\($i)]: purpose must be a non-empty string" end),
(if ($e.repos | type) != "array" or ($e.repos | length) == 0 or ([$e.repos[] | repo_ok] | all | not) then
"domains[\($i)]: repos must be a non-empty array of owner/repo"
elif $e.repos != ($e.repos | unique) then "domains[\($i)]: repos must be sorted and unique"
else empty end)
end),
([.domains[] | objects | .domain] as $d |
if $d != ($d | unique) then "domains must be strictly increasing by domain (sorted, none repeated)" else empty end)
end' "$REGISTRY")

# Each DOMAIN given must be registered.
for d in "$@"; do
jq -e --arg d "$d" 'any(.domains[]?; .domain == $d)' "$REGISTRY" >/dev/null 2>&1 \
|| errors+=("domain \"$d\" is not registered in $(basename "$REGISTRY")")
done

if [ ${#errors[@]} -gt 0 ]; then

Check failure on line 96 in scripts/check-uuid-v8-domains.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5sfGi8KAjJag5XWJ0&open=AaE5sfGi8KAjJag5XWJ0&pullRequest=1246
for e in "${errors[@]}"; do
echo "::error file=$REGISTRY::$e"

Check warning on line 98 in scripts/check-uuid-v8-domains.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Redirect this error message to stderr (>&2).

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5sfGi8KAjJag5XWJ1&open=AaE5sfGi8KAjJag5XWJ1&pullRequest=1246
done
exit 1
fi
echo "profile C domain register: $(jq '.domains | length' "$REGISTRY") domain(s) valid${*:+; required domain(s) registered: $*}"
82 changes: 82 additions & 0 deletions scripts/tests/check-uuid-v8-domains-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: MPL-2.0
#
# Proves scripts/check-uuid-v8-domains.sh passes the real ADR-008 profile C
# domain register and fails each planted defect for its own reason, so the
# gate can fail and says why.
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
CHECK="$ROOT/scripts/check-uuid-v8-domains.sh"
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
pass=0; fail=0

ok() { pass=$((pass+1)); echo " ok $1"; }

Check warning on line 13 in scripts/tests/check-uuid-v8-domains-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add an explicit return statement at the end of the function.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5se648KAjJag5XWJm&open=AaE5se648KAjJag5XWJm&pullRequest=1246
bad() { fail=$((fail+1)); echo " FAIL $1"; }

Check warning on line 14 in scripts/tests/check-uuid-v8-domains-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add an explicit return statement at the end of the function.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5se648KAjJag5XWJn&open=AaE5se648KAjJag5XWJn&pullRequest=1246

# expect <label> <exit> <message-fragment|-> <args...>
expect() {
local label=$1 want_rc=$2 want_msg=$3; shift 3
local out rc=0
out=$(bash "$CHECK" "$@" 2>&1) || rc=$?
if [ "$rc" -ne "$want_rc" ]; then bad "$label (exit $rc, want $want_rc): $out"; return; fi

Check failure on line 21 in scripts/tests/check-uuid-v8-domains-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5se648KAjJag5XWJo&open=AaE5se648KAjJag5XWJo&pullRequest=1246
if [ "$want_msg" != - ] && ! grep -qF -- "$want_msg" <<<"$out"; then

Check failure on line 22 in scripts/tests/check-uuid-v8-domains-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5se648KAjJag5XWJp&open=AaE5se648KAjJag5XWJp&pullRequest=1246
bad "$label (no \"$want_msg\" in: $out)"; return
fi
ok "$label"
}

# plant <name> <jq filter over the real register> — writes it as JCS + LF.
plant() { jq -cS "$2" "$ROOT/docs/decisions/ADR-008-profile-c-domains.json" > "$WORK/$1.json"; }

Check warning on line 29 in scripts/tests/check-uuid-v8-domains-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5se648KAjJag5XWJr&open=AaE5se648KAjJag5XWJr&pullRequest=1246

Check warning on line 29 in scripts/tests/check-uuid-v8-domains-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add an explicit return statement at the end of the function.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5se648KAjJag5XWJs&open=AaE5se648KAjJag5XWJs&pullRequest=1246

Check warning on line 29 in scripts/tests/check-uuid-v8-domains-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5se648KAjJag5XWJq&open=AaE5se648KAjJag5XWJq&pullRequest=1246

echo "the real register"
expect "is valid" 0 "domain(s) valid"
expect "registers the domains its repos mint" 0 "registered" proof-goal prove-result echidnabot-proof-obligation gv-clade-index
expect "an unregistered domain fails" 1 'domain "no-such-domain" is not registered' proof-goal no-such-domain

echo "planted defects, each named"
plant dup '.domains += [.domains[0]] | .domains |= sort_by(.domain)'
expect "a domain registered twice" 1 "strictly increasing" --registry "$WORK/dup.json"
plant unsorted '.domains |= reverse'
expect "domains out of order" 1 "strictly increasing" --registry "$WORK/unsorted.json"
plant colon '.domains[0].domain = "a:b"'
expect "a domain with \":\"" 1 "invalid domain" --registry "$WORK/colon.json"
plant space '.domains[0].domain = "a b"'
expect "a domain with a space" 1 "invalid domain" --registry "$WORK/space.json"
plant empty '.domains[0].domain = ""'
expect "an empty domain" 1 "invalid domain" --registry "$WORK/empty.json"
plant nopurpose '.domains[0] |= del(.purpose)'
expect "an entry without purpose" 1 "keys must be exactly" --registry "$WORK/nopurpose.json"
plant extra '.domains[0].owner = "x"'
expect "an entry with an extra key" 1 "keys must be exactly" --registry "$WORK/extra.json"
plant blankname '.domains[0].name = ""'
expect "an empty name" 1 "name must be a non-empty string" --registry "$WORK/blankname.json"
plant norepos '.domains[0].repos = []'
expect "no repos" 1 "repos must be a non-empty array" --registry "$WORK/norepos.json"
plant reporev '.domains[] |= (if (.repos | length) > 1 then .repos |= reverse else . end)'
expect "repos out of order" 1 "repos must be sorted and unique" --registry "$WORK/reporev.json"
plant badrepo '.domains[0].repos = ["not a repo"]'
expect "a repo that is not owner/repo" 1 "repos must be a non-empty array" --registry "$WORK/badrepo.json"
plant schema '.schema = "estate-uuid-v8-domains/2"'
expect "an unknown schema" 1 "schema must be" --registry "$WORK/schema.json"
plant toplevel '.note = "x"'
expect "an extra top-level key" 1 "top-level keys" --registry "$WORK/toplevel.json"

echo "planted byte-shape defects"
jq -S . "$ROOT/docs/decisions/ADR-008-profile-c-domains.json" > "$WORK/pretty.json"
expect "pretty-printed, not JCS" 1 "not in JCS form" --registry "$WORK/pretty.json"
jq -c . "$ROOT/docs/decisions/ADR-008-profile-c-domains.json" | sed 's/{"domains":\(.*\),"schema":\(.*\)}$/{"schema":\2,"domains":\1}/' > "$WORK/order.json"
expect "keys out of JCS order" 1 "not in JCS form" --registry "$WORK/order.json"
printf '%s' "$(cat "$ROOT/docs/decisions/ADR-008-profile-c-domains.json")" > "$WORK/nolf.json"
expect "no final LF" 1 "exactly one line" --registry "$WORK/nolf.json"
{ cat "$ROOT/docs/decisions/ADR-008-profile-c-domains.json"; echo; } > "$WORK/twolf.json"
expect "two final LFs" 1 "exactly one line" --registry "$WORK/twolf.json"
plant nonascii '.domains[0].purpose = "café"'
expect "non-ASCII text" 1 "printable ASCII" --registry "$WORK/nonascii.json"

echo "the check refuses to judge what it cannot read"
expect "a missing register" 2 "no register" --registry "$WORK/absent.json"
echo '{"domains": [' > "$WORK/broken.json"
expect "an unparseable register" 2 "does not parse" --registry "$WORK/broken.json"

echo "PASS=$pass FAIL=$fail"
[ "$fail" -eq 0 ]

Check failure on line 82 in scripts/tests/check-uuid-v8-domains-test.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE5se648KAjJag5XWJt&open=AaE5se648KAjJag5XWJt&pullRequest=1246
Loading