Skip to content

Bot identity, pubinfo tags, and migrate-pipeline fixes (v0.2.0) - #4

Merged
GertjanBisschop merged 9 commits into
eu-parc:mainfrom
knowledgepixels:main
Jun 25, 2026
Merged

Bot identity, pubinfo tags, and migrate-pipeline fixes (v0.2.0)#4
GertjanBisschop merged 9 commits into
eu-parc:mainfrom
knowledgepixels:main

Conversation

@tkuhn

@tkuhn tkuhn commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Brings the fork's main up to v0.2.0. Clean fast-forward over eu-parc/main (9 commits ahead, 0 behind), tests included and passing (96 passed).

Features

  • Bot identityfeat(introduction): a self-signed agent-introduction (key-declaration) nanopub builder + pubmate-bootstrap-identity CLI. Generates a bot keypair offline and declares the publishing agent, binding it to its signing key (mirrors the community declaredBy pattern).
  • Pubinfo tagsfeat(pubinfo): defining and supersession builders can tag every nanopub with npx:hasNanopubType (repeatable) and nt:wasCreatedFromTemplate, threaded through pubmate-migrate as --nanopub-type/--template (so Nanodash renders minted nanopubs with the matching assertion-template form).
  • Batch attributionfeat(migrate): --default-suggester attributes any term lacking its own suggester via prov:wasAttributedTo.
  • Readable serializationfeat(utils): bind well-known prefixes and lead with this:/sub:.

Fixes

  • fix(migrate): drop dangling references and report them as errors (a clean migration expects zero).
  • fix(bnode): short, deterministic blank-node labels (sub:_b1) instead of long random hashes.
  • fix(label): prefer the untagged literal for a nanopub's rdfs:label (e.g. "Lead" over "Lood"@NL-BE).
  • fix(signing): read key material from --private-key/--public-key as file paths.

Follow-up

After merge + a v0.2.0 release tag, the biochementity-vocabulary pubmate pin bumps from v0.1.0 to v0.2.0 (its make migrate target now uses --nanopub-type/--template).

🤖 Generated with Claude Code

tkuhn and others added 9 commits June 24, 2026 08:52
A published nanopub is immutable, so baking in a reference to a term that
does not exist (a dangling foreign key, e.g. left behind when a duplicate
compound was deduplicated upstream) is worse than omitting it: the link can
never resolve. Previously such references were kept with their old ids and
logged as a warning.

Now split_references drops dangling references from the minted graph, and
_resolve_all does the same in the superseding pass so a superseding nanopub
never reintroduces a broken link. The term still mints with its remaining
triples. migrate.py logs each dropped reference at ERROR (with the offending
target) plus a batch summary telling the curator to fix the source and re-run;
the run still completes (non-fatal), so a known-bad source link does not block
publishing the rest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnygThDJDwSVZg3kYujJKz
serialize_nanopub now binds a curated set of vocabulary prefixes (schema,
pehterms, biochementity, obo, skos, prov, dcterms) before serializing, so
namespaces render as meaningful prefixes instead of rdflib's auto-numbered
ns1:/ns2:. The header is ordered with this: and sub: first (the nanopub and
its sub-graphs anchor every file), then the rest alphabetically, so the .trig
reads top-down. Callers can extend or override via a new `prefixes` argument.

Purely a rendering change: binding happens at write time, after signing, so
trusty artifact codes and signatures are unaffected (verified is_valid).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnygThDJDwSVZg3kYujJKz
Add a generic, identity-agnostic way to bootstrap a publishing bot's
cryptographic identity, mirroring the nanopub-community declaredBy pattern:

- introduction.py: build_introduction() builds the self-signed key-declaration
  nanopub. The agent is a fresh URI minted inside the introduction itself,
  typed npx:Bot/npx:SoftwareAgent, frbr:owner-ed by a human ORCID, with its
  RSA public key declared. The agent self-URI only exists post-sign, so it (and
  npx:signedBy) use the ~~~ARTIFACTCODE~~~ placeholder, resolved at sign time
  like scheme A. Tagged nt:wasCreatedFromTemplate so Nanodash renders it nicely.
- cli/bootstrap_identity.py: pubmate-bootstrap-identity. Generates (or reuses) an
  RSA keypair and writes the signed introduction to --output-dir. Signs offline
  by default: does NOT publish and never touches a secret store, so the private
  key (the bot's identity) can be generated locally and reviewed before anything
  goes out. Refuses to overwrite an existing key; chmod 600 on the private key.
- utils.py: bind foaf/frbr/nt prefixes so introductions render readably.

Tests cover validity, self-signing, agent typing/ownership, the key declaration,
owner attribution, and the template tag (incl. opt-out).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnygThDJDwSVZg3kYujJKz
A term often carries an untagged label plus localized variants (e.g. "Lead" and
"Lood"@NL-BE). The nanopub's own rdfs:label (pubinfo) was taken from the first
rdfs:label rdflib happened to yield, so it sometimes picked a localized one.

Add preferred_label() (untagged > English > any, ties broken by text for
determinism) and use it for both the defining (term_input_from_assertion) and
superseding (migrate._label) labels. The assertion keeps every label variant —
only the nanopub's headline label changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnygThDJDwSVZg3kYujJKz
Source assertions use anonymous blank nodes ([ a pehterms:ContextAlias; … ]),
whose rdflib ids are long (e.g. ndf301ab1d67…b1) and regenerated each parse.
nanopub-py skolemizes each blank node to a sub:_<id> URI, so those long,
non-reproducible ids leaked into the published nanopub (sub:_ndf301ab1d67…b1).

Add relabel_blank_nodes(): rename blank nodes to short, content-ordered labels
(b1, b2, …) before each builder wraps the assertion, so they serialize as
sub:_b1. Wired into the defining and superseding builders. Pure relabeling (the
graph stays isomorphic; nanopub-py already skolemized these), and the labels are
assigned by a content key rather than the source's random ids, so the same input
always yields the same labels — and therefore reproducible artifact codes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnygThDJDwSVZg3kYujJKz
Add pubmate-migrate --default-suggester <orcid>, attributing prov:wasAttributedTo
on every term that carries none — the case when migrating an existing batch all
contributed by one person. Threads through to both the defining nanopubs (via the
minter's existing default_suggester_orcid) and the superseding ones. The suggester
goes in provenance, distinct from the bot signer; per-term wasAttributedTo (when
present) still wins.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnygThDJDwSVZg3kYujJKz
resolve_signing passed the CLI key-file path strings straight to nanopub.Profile,
which only reads a key file when given a pathlib.Path — a str is taken as the raw
key material, so signing crashed with "RSA key format is not supported". This hit
every real-key signing path (pubmate-migrate and pubmate-mint-publish); only the
testsuite-key path was exercised before. Pass Path objects so the files are read.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnygThDJDwSVZg3kYujJKz
…omTemplate

Add generic support for two Nanodash pubinfo hints on the defining and
supersession builders: npx:hasNanopubType (repeatable, the kind of thing
the nanopub asserts) and nt:wasCreatedFromTemplate (the assertion template
it follows, so Nanodash renders it with the matching form). Shared via a
new add_pubinfo_tags() helper; threaded through pubmate-migrate as
--nanopub-type/--template.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New features since v0.1.1: bootstrap-identity / introduction CLIs, pubinfo
hasNanopubType + wasCreatedFromTemplate tags, readable prefixes, and the
dangling-reference / blank-node / label fixes in the migrate pipeline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@GertjanBisschop
GertjanBisschop merged commit 75458fc into eu-parc:main Jun 25, 2026
3 checks passed
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.

2 participants