Skip to content

fix(sync): derive uuid from the repo's actual owner, not a hardcoded one - #48

Merged
hyperpolymath merged 1 commit into
mainfrom
fix/derive-uuid-from-actual-owner
Jul 16, 2026
Merged

fix(sync): derive uuid from the repo's actual owner, not a hardcoded one#48
hyperpolymath merged 1 commit into
mainfrom
fix/derive-uuid-from-actual-owner

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

The uuid is derived, not allocated. The spec defines it as

uuid = UUIDv5(namespace = URL, name = "github.com/<owner>/<name>")

so the owner segment is part of the identity. Both derivation sites hardcoded it to hyperpolymath:

  • sync/deploy-clade-a2ml.sh:80
  • sync/export-json.sh:28

Every estate repo is hyperpolymath-owned today, so the bug is invisible. It stops being invisible the moment a repo is hosted anywhere else: the script derives a uuid from an owner path that does not exist, writes it into CLADE.a2ml as fact, and the repo wears an identity belonging to nothing.

Found while creating metadatastician/cadastra. Its true uuid is:

uuidgen --sha1 --namespace @url --name "github.com/metadatastician/cadastra"
-> da97ea76-1715-5331-963b-27f011b10d22

but these scripts would have written:

uuidgen --sha1 --namespace @url --name "github.com/hyperpolymath/cadastra"
-> 78c31c34-79c3-584d-bc94-50064431ca33

Changes

  • parse-repos.sh — optional owner field, emitted as TSV column 7, defaulting to hyperpolymath. Existing entries need no edit. Last column, so consumers reading the first six fields are unaffected.
  • deploy-clade-a2ml.sh — takes owner as $7; uses it for the uuid, primary-owner, and all three forge lines. Now reads 7 fields (a 7th column would otherwise be absorbed into description).
  • export-json.sh — same fix. No new JSON key — schema unchanged on purpose, since github already carries owner/name.

Two related hazards fixed while here

  • Registry/remote disagreement is now loud. If repos.a2ml says one owner and git remote get-url origin says another, deploy warns and skips rather than silently deriving a wrong identity.
  • ALREADY checks both layouts. Two are in use: .machine_readable/CLADE.a2ml (this script + CLADE-001) and .machine_readable/descriptiles/CLADE.a2ml (rsr-template-repo, chronicles-of-slavia). Checking only the first meant a descriptiles-layout repo looked like it had no CLADE, so this script wrote a second one — two files, two identities, no error. Which layout is authoritative is an unresolved canon disagreement and yours to settle; until then, refusing to overwrite is the safe reading.

Verification

Clean worktree from origin/main:

  • repos.json regenerated before/after is BYTE-IDENTICAL — all 319 existing entries derive exactly as before (none sets an explicit owner).
  • ✅ The registry's own worked examples still reproduce: januskey → e216170e-…, gv-clade-index → c88e9ff3-…, rsr-template-repo → a5ea1382-….
  • ✅ A temporary entry with owner = "metadatastician" derives da97ea76-… and github "metadatastician/cadastra". The seed entry was reverted — registering cadastra is outward-facing and yours (doctrine 11), not this PR's.

Every uuid quoted was recomputed with uuidgen, not recalled.

Not addressed (pre-existing)

The committed worker/data/ artifacts do not match what export-json.sh producesrepos.json is pretty-printed in git but the generator emits compact (3646 vs 321 lines); index.json differs by ~1380 lines. Regenerating is a separate, owner-facing decision, so worker/data/ is deliberately untouched.

Draft: this changes how estate identity is derived, so it is for the owner to review and merge.

🤖 Generated with Claude Code

The uuid is DERIVED, not allocated — the registry spec defines it as

    uuid = UUIDv5(namespace = URL, name = "github.com/<owner>/<name>")

so the owner segment is part of the identity. Both derivation sites hardcoded
that segment to "hyperpolymath":

    sync/deploy-clade-a2ml.sh:80
    sync/export-json.sh:28

Every estate repo is hyperpolymath-owned today, so the bug was invisible. It
stops being invisible the moment a repo is hosted anywhere else: the script
derives a uuid from an owner path that does not exist, writes it into
CLADE.a2ml as fact, and the repo ends up wearing an identity that belongs to
nothing.

Found while creating metadatastician/cadastra. Its true uuid is

    uuidgen --sha1 --namespace @url --name "github.com/metadatastician/cadastra"
    -> da97ea76-1715-5331-963b-27f011b10d22

but these scripts would have written

    uuidgen --sha1 --namespace @url --name "github.com/hyperpolymath/cadastra"
    -> 78c31c34-79c3-584d-bc94-50064431ca33

derived from a github.com/hyperpolymath/cadastra that does not exist.

Changes:

* parse-repos.sh — optional `owner` field, emitted as TSV column 7, defaulting
  to "hyperpolymath". Existing entries need no edit and parse unchanged. It is
  the last column, so any consumer reading the first six fields is unaffected.
* deploy-clade-a2ml.sh — takes owner as $7; uses it for the uuid, primary-owner
  and all three forge lines. Reads 7 fields from the parser (a 7th column would
  otherwise have been absorbed into `description`).
* export-json.sh — same derivation fix. No new JSON key: the schema is unchanged
  on purpose, since the existing `github` field already carries "owner/name".

Two related hazards fixed while here:

* Registry/remote disagreement is now a loud failure, not a silent wrong
  identity. If repos.a2ml says one owner and `git remote get-url origin` says
  another, deploy warns and skips rather than deriving from the wrong one.
* ALREADY-exists now checks BOTH layouts in use across the estate:
      .machine_readable/CLADE.a2ml               (this script + CLADE-001)
      .machine_readable/descriptiles/CLADE.a2ml  (rsr-template-repo, chronicles-of-slavia)
  Checking only the first meant a descriptiles-layout repo looked like it had no
  CLADE, so the script wrote a SECOND one — two files, two identities, no error.
  Which layout is authoritative is an unresolved canon disagreement and the
  owner's to settle; until then, never overwriting an existing declaration is
  the safe reading.

Verified in a clean worktree from origin/main:

  * repos.json regenerated before and after the change is BYTE-IDENTICAL — all
    319 existing entries derive exactly as before (none sets an explicit owner).
  * The registry's own worked examples still reproduce:
        januskey          e216170e-ff47-5a5c-bbdd-15e61c8190c8
        gv-clade-index    c88e9ff3-9aac-5de8-b891-19b5de64aedd
        rsr-template-repo a5ea1382-a34c-5334-8a46-a2ebe904c810
  * A temporary entry with owner = "metadatastician" derives
    da97ea76-1715-5331-963b-27f011b10d22 and github "metadatastician/cadastra"
    — the correct values. The seed entry was then reverted: registering cadastra
    is outward-facing and the owner's act (doctrine 11), not this PR's.

Every uuid quoted above was recomputed with uuidgen, not recalled.

Not addressed (pre-existing, reported not fixed): the committed worker/data/
artifacts do not match what export-json.sh currently produces — repos.json is
pretty-printed in git but the generator emits compact (3646 vs 321 lines), and
index.json differs by ~1380 lines. Regenerating is a separate, owner-facing
decision, so worker/data/ is deliberately untouched here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath marked this pull request as ready for review July 16, 2026 22:30
@hyperpolymath
hyperpolymath merged commit 390b106 into main Jul 16, 2026
23 checks passed
@hyperpolymath
hyperpolymath deleted the fix/derive-uuid-from-actual-owner branch July 16, 2026 22:30
hyperpolymath added a commit that referenced this pull request Jul 17, 2026
…lds (#50)

Two problems, both in how a `CLADE.a2ml` gets written. The second one
**defeats my own merged #48** — details below.

## 1. A code cannot express a wrong belief, so it cannot be checked for
one

CLADE-003 checks the code is one of the 12. It **cannot check the author
meant that clade** — every code is valid whatever you thought it stood
for. So these passed every gate:

| repo | wrote | believing | `pt`/`gv` actually mean |
|---|---|---|---|
| paint-type | `primary = "pt"` | "**P**ain**T**-type" | Protocols &
Interop (it is an image editor → `ap`) |
| gossamer | `primary = "gv"` | "**G**raphical/**V**isual" | Governance
& Standards (there is no graphical clade → `dx`) |

**The two letters abbreviate the CLADE's name. They never abbreviate the
REPO's name.** It is a closed taxonomy of categories, not an
abbreviation scheme — but nothing said so at the point of authoring, and
nothing could catch it.

### Fix: write the name beside the code, and check the pair

```toml
[clade]
primary      = "ap"
primary-name = "Applications"
```

CLADE-006 (BLOCK, pre-commit) rejects any pair disagreeing with
`verisim/seed/clades.a2ml`. **The redundancy is the point** — the *pair*
can express the wrong belief, so the pair can be rejected. Verified on
all 10 cases, including both real historical errors:

```
ap|Applications              PASS
dx|Developer Ecosystem       PASS
gv|Governance & Standards    PASS   (ampersand survives)
pt|PainT-type                FAIL   <- the actual paint-type error
gv|Graphical/Visual          FAIL   <- the actual gossamer error
pt|Applications              FAIL   valid code, wrong name
ap|Protocols & Interop       FAIL   valid name, wrong code
zz|Applications              FAIL   invalid code
ap|<missing>                 FAIL   cannot omit the name
UNASSIGNED|UNASSIGNED        FAIL   template default fails loudly
```

`clades.a2ml` and the spec now state the rule and name both real errors,
so the next author reads *why* before choosing. `deploy-clade-a2ml.sh`
emits `primary-name` and refuses to write an identity it cannot name.

## 2. The TSV reader collapsed empty fields — 153 files already
corrupted

**Tab is IFS whitespace**, so `IFS=$'\t' read` treats a run of tabs as
**one** delimiter: empty fields vanish and everything after shifts left.
**316 of 319** seed entries have an empty `parent` — this was the normal
case, not an edge case:

```
proven <tab> fv <tab> [] <tab> standalone <tab><tab> Formally verified… <tab> hyperpolymath
                                                  ^^ empty parent collapses
-> parent="Formally verified…"   description="hyperpolymath"   owner=""
```

**The damage is real and committed — 153 CLADE.a2ml files** carry
`rationale = ""` with the repo's *description* sitting in `parent`:

```
panic-attack   parent = "Security scanning tool — static analysis…"
flat-mate      parent = "Flat/apartment sharing management…"
rpa-elysium    parent = "Robotic Process Automation framework…"
live-files     parent = "Live file synchronisation tool…"
```

### This defeated #48, and #48's own test could not see it

The `owner` support I added in #48 **never worked here**: `$7` was
always empty, so `owner` always fell back to `hyperpolymath` and an
explicit `owner = "metadatastician"` was ignored. #48's byte-identical
regression test passed because `export-json.sh` uses `awk -F'\t'`, which
does **not** collapse — the two derivation sites disagreed and only the
tested one was correct. My apologies: I shipped that.

### Fix

Translate tabs to US (`0x1f`) before reading. It is not IFS whitespace
so runs are not collapsed, it cannot occur in the data (0 occurrences in
`repos.a2ml`), and it is exactly what the ASCII unit separator is for.
The parser's TSV contract is unchanged.

## Verification

Scratch tree, end to end:

```
proven    (empty parent — the 316-entry case)
  rationale = "Formally verified safety library — Idris2 dependent types…"   (was "")
  parent    = ""                                                             (was the description)
  uuid      = ffa8e1f5-…                                                     (unchanged, matches registry)

cadastra  (owner = "metadatastician" in seed)
  uuid          = da97ea76-1715-5331-963b-27f011b10d22    <- correct; #48 works for the first time
  primary-owner = "metadatastician"
```

Also: `repos.json` regenerates **byte-identical** (no uuid moves), both
a2ml files still parse as **TOML** (the dogfood gate uses `tomllib`),
and the spec renders under asciidoctor.

## Not in this PR

Repairing the **153 already-corrupted files** is a separate,
owner-facing job — this only stops the script producing more. Related:
#49.

Draft: this changes the clade contract for every repo, so it is yours to
review.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit to metadatastician/gossamer that referenced this pull request Jul 17, 2026
…tradicted itself (#113)

This file declared two things that cannot both be true:

```toml
primary-owner   = "hyperpolymath"
[forges] github = "metadatastician/gossamer"
```

The uuid was correctly derived from the owner it declared:

```
uuidgen --sha1 --namespace @url --name "github.com/hyperpolymath/gossamer"
-> 62caa738-137e-5390-bdff-453f475e94b7      # the old value
```

**but that URL 404s.** gossamer does not exist in the hyperpolymath org.
It exists at `metadatastician/gossamer` — which is what `git remote
get-url origin` says, and what `[forges]` already said. So gossamer's
identity was derived from a repo that is not there.

**Not a typo.** `gv-clade-index/sync/deploy-clade-a2ml.sh:80` hardcoded
the owner segment to `hyperpolymath` for every repo it wrote, so any
repo hosted elsewhere got an identity belonging to nothing. gossamer is
a live victim. Fixed upstream in hyperpolymath/gv-clade-index#48.

## The fix

```
uuid = UUIDv5(URL, "github.com/<owner>/<name>")

uuidgen --sha1 --namespace @url --name "github.com/metadatastician/gossamer"
-> 16eef45c-0690-5b12-8018-904891430cae
```

Method verified by reproducing the registry's own worked examples
byte-for-byte first (`januskey`, `gv-clade-index`, `rsr-template-repo`).

Nothing depended on the old value — gossamer is in neither the seed nor
`worker/data/repos.json`. **Not registered** by this PR; that is
outward-facing and yours (doctrine 11).

✅ Verified: the uuid now **self-checks**, and `primary-owner`,
`[forges]` and the real git remote all agree.

## Separate problem, NOT fixed here — needs your call

The clade rationale reads:

> `rationale = "Gossamer is a webview/GUI-layer project; gv
(graphical/visual) clade."`

But in the registry taxonomy (`verisim/seed/clades.a2ml`), **`gv` is
Governance & Standards** — "licensing, compliance, policy enforcement".
There is no "graphical/visual" clade. So the rationale appears to have
picked `gv` from a misreading of the code, and a webview/GUI project
would more plausibly be `ap` (Applications) or `dx` (Developer
Ecosystem).

That also makes `prefixed-name = "gv-gossamer"` suspect. I left all of
it untouched: the clade is a judgement, not a derivation, and it is
yours to ratify.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant