Skip to content

Merge the package conventions companion repository into the template - #17

Merged
adelahaye-ecc merged 14 commits into
developfrom
feature/package-conventions
Oct 6, 2026
Merged

adelahaye-ecc merged 14 commits into
developfrom
feature/package-conventions

Conversation

@seebi

@seebi seebi commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Merges the eccenca-marketplace-packages-conventions companion repository into the template. The conventions become shipped skills, and the checker toolkit becomes something every generated package runs.

The companion repository is not referenced from anywhere in the template, so it can be archived without leaving a dead link.

What generated packages get

bin/ — five offline RDF checks. No Corporate Memory connection, no credentials, no install. They carry PEP 723 inline metadata and a uv run --script shebang, so there is no install step and no Python project in the generated repository.

check what it answers
check_dangling.py sh:property / sh:node / sh:group / sh:sparql / shui:valueQuery references that point at nothing
check_placeholders.py placeholder declarations: keys claimed twice, parameters with no declaration, parameters inside a value query, keys in braces inside a comment
check_paths.py every property shape's shacl:path against the vocabulary, forwards and under shui:inversePath
audit_queries.py shipped queries against the documenting-a-query convention, and projection-variable capitalisation
check_all.py all four over a package directory, classifying files by what they declare

task check:offline runs them. task check and task import both run it first — import because that is the task that puts a catalog on a live instance, where a dangling reference makes the SHACL service answer HTTP 500 for every graph on the deployment.

Two new skills, bringing the shipped set to six:

  • vocabulary (all package types) — predicate order, the skos:definition substitution principle and what belongs in rdfs:comment instead, head-final class names, classifying by intention, plus a reference on the foaf:depiction every class wants.
  • catalog-queries (project packages) — which column Corporate Memory takes as the value, why a projection variable may not be capitalised, how to document a query, with references for placeholders and the ?graph column.

Existing skills extended — package-content gains graph ownership (listing a graph is what makes uninstall clean) and a four-graph architecture reference; shapes gains cardinalities, slugs, shacl:name vs rdfs:label and a paths.md reference.

What this means for the fleet

Nothing changes for any package until somebody runs copier update on it. When they do:

27 of 35 packages are unaffected. They ship no shape catalog — almost all are vocabulary packages — so every check is a no-op and task check:offline passes with a note.

Of the 8 packages with a catalog, 2 pass and 6 go red. Measured by running this branch's checkers over the fleet as it stands today:

package result blocking findings warnings
ecc-northwind-project-package pass — 0
ecc-filesystem-vocab-package pass — 8
ecc-supply-chain-risk-project-package FAIL 16× capitalised projection variable 85
ecc-police-demo-project-package FAIL 2× key inside a comment; 2× path not type-correct 28
ecc-product-data-project-package FAIL 3× key inside a comment; 1× capitalised projection variable 34
ecc-useful-queries-package FAIL 8× key inside a comment; 4× value query carrying a parameter 16

58 blocking findings, 286 warnings. Every blocking finding names something that is actually wrong:

  • 35 capitalised projection variables. Corporate Memory takes the value column as ?resource, or the first projected variable when ?resource is absent — a capital defeats that selection and the row renders the wrong value.
  • 16 placeholder keys written inside a comment. Substitution is a plain text replace over the whole query text, comments included, so these are rewritten too; a value query mentioning its own key becomes circular and its picker never fills.
  • 4 value queries carrying their own parameter. A value query runs in order to produce values for a parameter, so a parameter of its own can never be resolved first.
  • 4 shacl:paths that are not type-correct, each one silently disabling shacl:class, shacl:nodeKind and every cardinality on its row.

Why most findings only warn

The first version of this branch failed on everything the checkers report. Run over the fleet, that turned 7 of 8 catalogs red out of 229 findings, 186 of which were documentation — and one package failed with nothing actually wrong with it.

The largest single cause was "built-in key has no placeholder declaration", 63 findings. That flags nothing broken: {{shuiResource}}, {{shuiMainResource}} and {{shuiGraph}} are substituted by the form renderer from context whether or not a placeholder exists, so the declaration only makes a query runnable in the query editor. Across the whole fleet 1 file declares a QueryPlaceholder while 7 use shuiResource — it was failing packages for not adopting one package's optional editor aid.

So the split is: fail where something is broken, warn where prose is missing. A missing dcterms:description, a header that does not list the projection, and an undeclared built-in key all print as warnings. An undeclared custom key still fails, because nothing would ever substitute it.

What a maintainer has to do

For the 6 red packages: rename the capitalised projection variables, move the placeholder keys out of comments, give the value queries a literal graph, and fix the four paths. The warnings can be worked through at leisure — they do not gate anything.

Nothing forces the update. A package stays on its current _commit until somebody chooses to move it.

Two bugs found while surveying the fleet, and fixed here

  • A .ttl no parser accepts was skipped, not failed. cmemc package build ignores RDF syntax entirely, so this was precisely the gap the gate exists to cover. Now a failure, raised before the no-catalog pass, so a package whose only catalog is unparseable cannot slip through as "nothing to check".
  • A file declaring only shui:SparqlQuery classified as neither shapes nor vocabulary and was skipped. This cut both ways: ecc-useful-queries-package ships 14 queries and no shapes, so the two checks that are entirely about queries never ran on the one package built to hold them; and in ecc-product-data-project-package, where the query texts live in pdd-queries.ttl and the shapes in pv-shapes.ttl, the shapes' query targets looked textless — a false positive, now gone.

What was deliberately not merged

The source document carried the evidence that proved its rules: a FROM / FROM NAMED measurement table against one package's 1526-triple data graph, counts such as "19 features and 200 materials", a rejected colour pair, and two findings whose cause was explicitly never established. None of it migrated.

The test is whether a reader can reproduce it in their package. Evidence about the platform stays — "0 of 236 paths in eccenca's own system catalog are blank nodes" is the same category as the existing "21 of its node shapes use rdfs:comment and one uses the other". Evidence about one package's data does not. Unresolved questions were dropped outright.

This is recorded under Deliberate decisions in CLAUDE.md, so it does not come back as a review comment.

Verification

  • task check passes end to end against docker.localhost: build → install --replace → uninstall, both test cases, exit 0.
  • New check:offline:case drives the rendered package's own bin/ over tests/fixtures/good and tests/fixtures/bad, and requires each checker to catch its own planted fault by name. An exit-code assertion alone would be satisfied by one checker failing while three others had gone blind — verified by blinding check_dangling.py, which the exit code alone does not catch.
  • check-skills.py and the shipped Stop hook know about bin/ and both new skills; catalog-queries is asserted absent from a vocabulary package.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EvKf2rhUMK7hSorGsaVQz8

@sebastian-siemoleit

Copy link
Copy Markdown

I compared the shipped skills against the conventions repository this PR merges. Almost everything carried over, and dropping the per-package evidence is well argued. Three things before this merges:

1. The dirty-tree warning was lost (package-content/SKILL.md, The version is never written by hand)

The section gives the git describe --tags --always --dirty formula and says the repository needs at least one commit. What the conventions said, and this no longer does, is the consequence: building from a dirty tree yields <id>-v0.0.0-<sha>-dirty.cpa — an archive whose version names a state that lives on no commit and can never be rebuilt. So: commit before task build. Worth one sentence, since nothing else stops it.

2. The namespace-dependency rule ships to packages that cannot follow it (package-content/SKILL.md, Dependencies have to cover every namespace the graphs use)

package-content is not conditional, so vocabulary packages get it too, and it tells them to add a marketplace-package dependency for every namespace their graphs use. They can't: cmemc 26.2.1 rejects a vocabulary manifest with any dependency at build time —

Value error, Vocabulary Packages do not allow dependencies.

— which agrees with the Vocabulary packages are asked no dependency questions decision. (The published schema at https://eccenca.market/api/manifest does not express this: VocabularyPackageManifest has an unconstrained dependencies array, so manifest_check would not catch it either.) Suggest scoping the section to project packages.

3. Two editorial slips

  • build-projects/SKILL.md: the cmemc project export --extract --replace --without-userdata … block appears twice in a row (directly after the import/export pair, then again on its own).
  • catalog-queries/references/graph-column.md, last bullet of When to prefer the computed binding: headed "Keep the echo where no hop is defining", but its last sentence says to record "which hop was wrapped and why" — which applies to the opposite case. In the source these were two statements: keep the echo where no hop is defining; where a hop is wrapped on a derived relation, record which and why. Splitting the bullet restores that.

Four findings from the review of the conventions merge, all in the shipped
skills:

- `package-content` now says to commit before `task build`. A dirty tree makes
  `git describe` append `-dirty`, so the archive is named for a state that
  lives on no commit and can never be rebuilt. Nothing else stops it.
- the namespace-dependency section is scoped to project packages. The skill is
  not conditional, so vocabulary packages read it too, and `cmemc` rejects a
  vocabulary manifest carrying any dependency - which is the existing
  deliberate decision, seen from the build side. The published manifest schema
  does not express the restriction, so `manifest_check` does not catch it.
  SKILL.md is not Jinja-rendered, so the scoping is prose rather than a
  conditional block.
- `build-projects` had the export command twice in a row; the stray second
  block is gone.
- the last bullet of *When to prefer the computed binding* carried two
  statements under one heading, and its heading contradicted its last sentence.
  Split back into keeping the echo where no hop is defining, and recording
  which hop was wrapped where one is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018zcpmL3gB5z2RvTMjrmoow

@sebastian-siemoleit sebastian-siemoleit left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The three points from my earlier comment (#17 (comment)) are all addressed in 3e98d15: the dirty-tree warning is back in package-content, the namespace-dependency section is scoped to project packages, and both editorial slips are fixed. Ready to merge.

Further platform findings from the home library and variant config packages will come as a separate follow-up PR after this one merges.

@seebi
seebi requested a review from adelahaye-ecc October 6, 2026 13:52
@adelahaye-ecc

Copy link
Copy Markdown
Contributor

LGTM as well

@seebi
seebi removed the request for review from rpietzsch October 6, 2026 13:52
@adelahaye-ecc
adelahaye-ecc merged commit 2016fc6 into develop Oct 6, 2026
1 check passed
@adelahaye-ecc
adelahaye-ecc deleted the feature/package-conventions branch October 6, 2026 13:54
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.

3 participants