You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SAP-3014 needs a deterministic, versioned AgentFacts normalization contract for per-agent factual metadata. The contract must key all facts by the authoritative package inventory agentKey, preserve missing and partial extraction as unknown/partial facts, and avoid semantic invention or package-wide invocation/provenance work reserved for later Wave 1 issues.
Summary and scope
Add package AgentFacts protocol 1 in @sapiom/agent, parallel to PackageInventory and package graph evidence.
Normalize snapshots against the exact PackageInventory version and emit one record for every inventory agentKey.
Support authored descriptions, exact input/output JSON Schemas, declared capabilities, separately labeled observed capabilities from allowlisted capability-call observations, direct/source/evidence references, completeness diagnostics, and deterministic template summaries.
Diagnose and ignore unsupported or invalid observed facts, invalid cards, unknown card agent keys, duplicate cards, missing cards, sparse/partial extraction, dynamic/unknown data, and invalid schema structure without removing agent nodes.
Treat duplicate cards for the same inventory agentKey as an unknown/conflicted agent record so conflicting inputs remain byte-identical regardless of source order.
Downgrade sparse cards with unknown supported fields and agent-scoped dynamic-data diagnostics to partial with diagnostics instead of silently claiming complete extraction.
Validate declared JSON Schema fields with Ajv 2020-12 meta-schema validation, including nested keyword structure such as properties and required, while preserving boolean schemas as valid. Invalid schemas are preserved as exact data but labeled validation: invalid and force partial completeness.
Enforce exported record/snapshot invariants so a complete record cannot contain unknown fields, invalid schemas, or agent-scoped diagnostics that require partial extraction.
Harden packageAgentFactsSnapshotSchema.safeParse so nested invariant failures are reported as refinement issues instead of thrown from superRefine.
Add canonical JSON and stable snapshot IDs for byte-identical normalized output from equivalent inputs.
Document the public contract and add a changeset for @sapiom/agent.
Out of scope: package-wide invocation resolution, provenance/feeds analysis, local publication, deploy transport, persistence, UI, runtime lineage, and LLM semantic candidates.
Independent review found duplicate-card order dependence, sparse cards claiming complete extraction with unknown fields, agent-scoped dynamic-data diagnostics not downgrading records, exported schema parsers accepting impossible complete records, invalid JSON Schema type tokens being treated as validated facts, incomplete JSON Schema meta-schema validation, and nested snapshot safeParse throws. All have targeted regression coverage.
Current gaps / follow-up verification
No known acceptance-criterion gaps in the implemented protocol slice. Workspace-wide build/lint/test was not run because SAP-3014 changes are isolated to @sapiom/agent and the review window is timeboxed.
AI assistance
Codex implemented the approved issue-scoped slice, added focused contract fixtures, performed the listed verification, and incorporated independent review findings.
No confidentiality issues: the changeset, README section, and fixtures are
provider-neutral (workspace-acme, sapiom.agent-card), and ajv@^8.12.0
matches the range already used by @sapiom/harness and @sapiom/agent-runtime,
which also import ajv/dist/2020.js the same way. Changeset level (minor,
additive) is right.
Findings
1. $schema in a card makes createPackageAgentFactsSnapshot throw instead of labelling the schema invalid
packages/agent/src/package-agent-facts.ts:jsonSchemaValidation calls jsonSchemaMetaValidator.validateSchema(value) with the raw card value. Ajv 8's validateSchema reads schema.$schema and:
throws Error: $schema must be a string when $schema is not a string;
delegates to this.validate($schema, schema), which throws Error: no schema with key or ref "<uri>" when the URI is not registered on
the instance. Ajv2020 registers only https://json-schema.org/draft/2020-12/schema.
packageAgentFactsCardInputSchema accepts any JSON object, so $schema reaches
it unfiltered. A card carrying the most common value in the wild — {"$schema": "http://json-schema.org/draft-07/schema#", "type": "object"} —
throws out of recordFromCard and aborts the whole snapshot, taking every other
agent's facts with it. That is exactly the failure mode the protocol promises it
never has ("Invalid schemas are preserved as exact data but labeled validation: invalid"; "without removing agent nodes"). No test exercises $schema at all. Wrap the call in try/catch and treat a throw as validation: "invalid", or strip/normalize $schema before meta-validation.
2. Two byte-identical cards for one agent erase every fact for that agent
createPackageAgentFactsSnapshot treats cards.length > 1 as duplicate-card
and replaces the record with unknownRecord(...) — including when the cards are
identical. An extractor that reaches the same card through two discovery roots
(a symlinked path, a re-export, one card listed in two source manifests) loses
description, both schemas, and all capabilities for that agent and gets nothing
back explaining that the inputs never actually conflicted. Deduplicating by
canonical JSON first — you already have canonicalPackageAgentFactsJson and normalizeByCanonical — keeps order-independence while limiting the destructive
path to genuine conflicts.
3. Authored per-agent diagnostics can be dropped from snapshot.diagnostics
In mergeIncompleteCompleteness, the diagnostics.length === 0 branch returns diagnostics: [], so when a card supplies every supported field and an
authored completeness: { status: "partial", diagnostics: [...] }, those
diagnostics survive on the record but never reach the snapshot-level diagnostics array. Every other path aggregates them. A consumer that reads snapshot.diagnostics to decide whether extraction was trustworthy — the
obvious use of a snapshot-level array — sees a clean snapshot containing a
partial agent. The existing test for authored partial completeness also omits inputSchema/outputSchema/observed, so it takes the other branch and misses
this.
The same asymmetry applies to input diagnostics scoped to an agent with a
missing or duplicated card: inputDiagnosticsByAgentKey is consulted only in recordFromCard, so those agents' records carry missing-card/duplicate-card
but not the caller's dynamic-data.
4. 28 new public exports with no consumer, including a general-purpose JSON serializer
The barrel adds 13 values and 15 types; nothing in the repo imports any of them
outside index.ts. Two are worth reconsidering before they become permanent
semver commitments:
canonicalPackageAgentFactsJson(value: unknown) is a standalone canonical-JSON
encoder that happens to live in this module. Published, it will be used as
one, and its documented behavior is then locked: throws TypeError on class
instances, undefined fields, cycles, and non-finite numbers. If the protocol
only needs it internally, keep it internal or name it for what it is.
packageAgentFactsCardInputSchema is the extractor-facing input shape. If
extractors are expected to stay in-repo for now, exporting the input schema
invites third-party extractors and pins the card format harder than the
changeset implies.
Neither is a defect; both are contract decisions that are cheap now and
expensive after the minor release.
Nits
README.md describes the protocol but shows no call to createPackageAgentFactsSnapshot; the one behavior a consumer must know — the
hard throw when scope does not match inventory.version — is undocumented
outside the test.
agentKey: z.string() in both the card and record schemas allows ""; the
inventory is the only thing keeping it non-empty.
Verdict
Request changes — finding 1 is a crash on ordinary input that contradicts the
protocol's core guarantee, and findings 2 and 3 are silent data loss in the
normalized output. Findings 1–3 were reasoned from Ajv 8 source and the diff; node_modules is not installed in this checkout, so add the $schema case as a
regression test to confirm before fixing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Primary change type
Problem and motivation
SAP-3014 needs a deterministic, versioned AgentFacts normalization contract for per-agent factual metadata. The contract must key all facts by the authoritative package inventory agentKey, preserve missing and partial extraction as unknown/partial facts, and avoid semantic invention or package-wide invocation/provenance work reserved for later Wave 1 issues.
Summary and scope
Out of scope: package-wide invocation resolution, provenance/feeds analysis, local publication, deploy transport, persistence, UI, runtime lineage, and LLM semantic candidates.
Related work
Validation
Review fixes
Independent review found duplicate-card order dependence, sparse cards claiming complete extraction with unknown fields, agent-scoped dynamic-data diagnostics not downgrading records, exported schema parsers accepting impossible complete records, invalid JSON Schema type tokens being treated as validated facts, incomplete JSON Schema meta-schema validation, and nested snapshot safeParse throws. All have targeted regression coverage.
Current gaps / follow-up verification
No known acceptance-criterion gaps in the implemented protocol slice. Workspace-wide build/lint/test was not run because SAP-3014 changes are isolated to @sapiom/agent and the review window is timeboxed.
AI assistance
Codex implemented the approved issue-scoped slice, added focused contract fixtures, performed the listed verification, and incorporated independent review findings.