Skip to content

feat(reference): publish what an artifact CONTAINS, not just that it has a shape - #663

Open
beyondnetPeru wants to merge 2 commits into
mainfrom
feat/core-publishes-artifact-fields
Open

feat(reference): publish what an artifact CONTAINS, not just that it has a shape#663
beyondnetPeru wants to merge 2 commits into
mainfrom
feat/core-publishes-artifact-fields

Conversation

@beyondnetPeru

Copy link
Copy Markdown
Contributor

Closes the half of the artifact registry a satellite could not use.

The gap

The registry names an artifact and points at its schema's $id. A consumer therefore learns that a PRD is required in discovery and still cannot find out what a PRD contains — an $id is an identity, deliberately not a location, and nothing dereferences it.

That is not academic. evolith_tracker evaluates gate criteria against a flat field map: a criterion resolves a fieldPath and compares it. With no fields there is nothing to resolve, so a tenant can configure a rule over a document and nothing will ever read it — the gate checks that a file exists and never what it says.

Measured on a live tenant before this change:

Artifacts in the catalogue 34
Field schemas with any field 0
Criteria configured on any phase 0

What this does

Each registry entry now carries its fields, derived from the JSON Schema this repo already ships.

The schemas are not rewritten to a flat shape. They stay the source and this is a projection, so a schema change propagates on the next read instead of needing a second file kept in sync.

Across the corpus: 529 fields from 50 schemas. The PRD alone goes from nothing to 18, with types and requiredness:

metadata.identifier          text     required
metadata.status              enum     required   (Draft | In Review | Approved | Superseded)
executiveSummary.problemStatement    text  required
…

Three decisions worth reviewing

The type vocabulary is small on purposetext, rich-text, number, date, boolean, enum, url. It is exactly what the consumer's criterion operators can judge. A type outside it produces a field no criterion can evaluate, which is worse than a missing one: it can be selected in a rule and never satisfied.

Collections are omitted, and reported. gte, in-set and regex all assume a single value, so a list has no operator that can judge it. They are not dropped in silence: omittedFields says which and why, so someone counting 13 sections against 18 fields can see the difference is arrays and not a truncated schema. Corpus-wide that is 248 paths, all declared.

An unreadable schema costs one artifact, not the catalogue. A malformed file leaves that entry without fields rather than failing the whole registry, which every other artifact depends on.

Verification

  • 7 new unit tests over the derivation, including what it refuses to publish
  • Full core-api suite: 32 suites, 170 tests, green
  • Built into an image, deployed into a kind cluster, and read end to end by the satellite: GET /phases/discovery/artifacts returns the PRD with its 18 fields, and the Tracker's artifact-field-schemas/prd — which used to answer baseFields: [] — now resolves them

Ordering

The satellite side is a separate PR in evolith_tracker. This one should land first: with the Tracker's change deployed against an older Core, its artifacts simply have no fields again until this follows.

🤖 Generated with Claude Code

…has a shape

The registry names an artifact and points at its schema's $id. A consumer
therefore learns that a PRD is required in discovery and still cannot find out
what a PRD is supposed to contain: an $id is an identity, deliberately not a
location, and nothing dereferences it.

That gap is not academic. The satellite waiting on it evaluates gate criteria
against a flat field map, and with no fields a tenant can configure a rule over
a document that nothing will ever read — the gate ends up checking that a file
exists and never what it says. Measured on a live tenant before this: 34
catalogued artifacts, zero field schemas with any field, zero criteria.

So each registry entry now carries its FIELDS, derived from the schema the Core
already ships. The schemas are not rewritten flat: they stay the source and this
is a projection, so a schema change propagates on the next read instead of
needing a second file kept in sync. Across the corpus that is 529 fields from 50
schemas — the PRD alone goes from nothing to 18, with types and requiredness.

The type vocabulary is small on purpose: exactly what the existing criterion
operators can judge. A type outside it yields a field no criterion can evaluate,
which is worse than a missing one because it can be selected and never
satisfied. Collections are omitted for the same reason — gte, in-set and regex
all assume a single value — and REPORTED rather than dropped quietly, so someone
counting 13 sections against 18 fields can see the difference is arrays and not
a truncated schema.

An unreadable schema leaves that one artifact without fields instead of failing
the registry: one malformed file must not take down the catalogue every other
artifact needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: aarroyo <beyondnet.peru@gmail.com>
@beyondnetPeru
beyondnetPeru requested a review from a team as a code owner August 23, 2026 15:11
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

Copy link
Copy Markdown

📊 Bilingual Coverage Impact

PR Changes

  • Paired EN/ES files modified: 0
  • New EN files needing ES translation: 0

Repository Coverage

Metric Value
Total EN files 527
Total ES files 497
Paired files 0
Coverage 0%

Good: All EN changes have ES counterparts.


Generated by GitHub Actions

The labels these produce become the labels of a FORM — the satellite renders one
input per field — so the case matters. Title Case makes a form read like a menu
of commands rather than a set of questions, and it clashes with the sentence
case the rendering surfaces use everywhere else; two cases on one screen look
like two systems sharing it.

The acronym list exists because there is no rule to replace it. Lowercasing
every word turns `technicalFeasibilityId` into a label ending in "id", which
reads as a mistake, and leaving the camel case alone gives "Id", which reads as
a typo. Nothing in the spelling separates `id` from `is`, so the terms that get
shouted are named one by one. The list is short deliberately: a term missing
from it comes out as an ordinary word, which is merely plain, while a term
wrongly in it comes out shouting.

None of this runs for a schema that publishes a `title`. That is words chosen by
whoever owns the shape, and no amount of string-splitting here improves on them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: aarroyo <beyondnet.peru@gmail.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