mirror of
https://github.com/boshu2/agentops.git
synced 2026-09-14 15:08:13 +08:00
chore(provenance): align relation enum to W3C PROV-O vocabulary (ag-lmdx.7 #prov-o-vocabulary) (#660)
## What Rename the provenance ledger relation enum from AgentOps-local `subject_verb_object` names to the standard **W3C PROV-O / PROV-DM** verbs so an external auditor recognizes the term, and document the **columns-not-JSON-paths** guard-read principle in the schema. Schema changes are single-writer on `main`. Implements ag-lmdx.7. ### Relation mapping (atomic contract change) | Prior AgentOps-local | W3C PROV-O | |---|---| | `decision_produces_artifact` | `wasGeneratedBy` | | `decision_authorizes` | `wasAssociatedWith` | | `artifact_derived_from` | `wasDerivedFrom` | | `scenario_covers_artifact` | `wasInformedBy` | | `verdict_attests_artifact` | `wasAttributedTo` | | `bead_scopes_decision` | `wasInfluencedBy` | | `commit_implements_decision` | `wasRevisionOf` | | `learning_revises_decision` | `wasInvalidatedBy` | ## Scenarios - **Relations use PROV-O vocabulary** — the enum now contains only PROV-O verbs; an edge with the colloquial `derives_from` (or the prior `artifact_derived_from`) is REJECTED in favor of `wasDerivedFrom`. Enforced by two new bats cases (accept PROV-O, reject legacy vocabulary). - **Guard-read field is a column not a JSON path** — the schema description now states that guard-read/queryable fields (`from_id`, `to_id`, `relation`, `trust_tier`, the hash-chain anchors) are first-class top-level columns, never nested JSON payload paths, because Dolt JSON-path generated-column indexing is unreliable. The `judge_id` verdict guard already reads a first-class struct field in `evidencedturn`; **no Dolt migration is invented** (no Dolt projection schema exists in this repo — the principle is encoded in the contract). ## Ripple (every merged consumer, atomic) - `schemas/agentops-sdlc-provenance.v1.schema.json` (enum + description) - `cli/internal/provenancegraph/edge.go` (`Relations` + godoc) - schema-driven validator via `tests/scripts/validate-provenance-ledger.bats` (PROV-O accept + legacy reject) - consumers: `drrebuild`, `drwitness`, `evidencedturn`, `cmd/ao/provenance_*`, `cmd/ao/turn_verify` - all hash-chained fixtures **re-sealed via the canonical hasher** (`drrebuild` ledger + frozen `expected-graph-hash.txt`, `committed-witness.jsonl`); witness dolt-rows + provenance JSON fixtures updated - generated `cli/docs/COMMANDS.md` regenerated ## Verification - `cd cli && go build ./... && go vet ./...` clean; **`go test ./...` → 11942 passed in 72 packages** - `bats validate-provenance-ledger.bats witness-dolt-jsonl-crosscheck.bats` → 20/20 - `bats provenance-orphan-fixtures.bats check-provenance-orphans.bats` → 7/7 - `scripts/check-contracts-structural-floor.sh` → PASS (45 contracts) - `docs/provenance/ledger.jsonl` does not exist (no real entries to migrate); `.agents/ao/provenance/graph.jsonl` is a separate transcript-mining graph (no `relation` field) and out of scope. Closes-scenario: ag-lmdx.7#prov-o-vocabulary Bounded-context: BC4-Factory Evidence: schemas/agentops-sdlc-provenance.v1.schema.json
This commit is contained in:
@@ -54,9 +54,9 @@ evidence, and trust tier is a no-op (no duplicate row).
|
||||
|
||||
Examples:
|
||||
ao provenance add ag-x31t.4 cli/cmd/ao/provenance_add.go \
|
||||
--relation decision_produces_artifact --to-type artifact
|
||||
--relation wasGeneratedBy --to-type artifact
|
||||
ao provenance add soc-byl.3 ag-x31t \
|
||||
--relation bead_scopes_decision --from-type bead --to-type decision \
|
||||
--relation wasInfluencedBy --from-type bead --to-type decision \
|
||||
--trust-tier authored --evidence .agents/council/2026-05-30-debate-provenance-substrate.md`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
RunE: runProvenanceAdd,
|
||||
@@ -72,7 +72,7 @@ Examples:
|
||||
ao provenance list
|
||||
ao provenance list --json
|
||||
ao provenance list --from-id ag-x31t.4
|
||||
ao provenance list --relation decision_produces_artifact`,
|
||||
ao provenance list --relation wasGeneratedBy`,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: runProvenanceList,
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func init() {
|
||||
provenanceCmd.AddCommand(provenanceAddCmd)
|
||||
provenanceCmd.AddCommand(provenanceListCmd)
|
||||
|
||||
provenanceAddCmd.Flags().StringVar(&provAddRelation, "relation", "", "Typed relation (required), e.g. decision_produces_artifact")
|
||||
provenanceAddCmd.Flags().StringVar(&provAddRelation, "relation", "", "Typed PROV-O relation (required), e.g. wasGeneratedBy")
|
||||
provenanceAddCmd.Flags().StringVar(&provAddFromType, "from-type", "decision", "Source node type (decision|artifact|bead|...)")
|
||||
provenanceAddCmd.Flags().StringVar(&provAddToType, "to-type", "artifact", "Target node type (decision|artifact|bead|...)")
|
||||
provenanceAddCmd.Flags().StringVar(&provAddTrustTier, "trust-tier", "authored", "Trust tier (authored|inferred|mined)")
|
||||
|
||||
@@ -45,7 +45,7 @@ func chdirRepoFixture(t *testing.T) string {
|
||||
|
||||
// resetProvAddFlags sets the add flags to a known baseline for a test.
|
||||
func resetProvAddFlags() {
|
||||
provAddRelation = "decision_produces_artifact"
|
||||
provAddRelation = "wasGeneratedBy"
|
||||
provAddFromType = "decision"
|
||||
provAddToType = "artifact"
|
||||
provAddTrustTier = "authored"
|
||||
@@ -169,7 +169,7 @@ func TestProvenanceList_FiltersByFromIDAndRelation(t *testing.T) {
|
||||
}
|
||||
// Edge 2: a different decision, different relation.
|
||||
resetProvAddFlags()
|
||||
provAddRelation = "decision_authorizes"
|
||||
provAddRelation = "wasAssociatedWith"
|
||||
provAddToType = "bead"
|
||||
c2, _ := provTestCmd()
|
||||
if err := runProvenanceAdd(c2, []string{"soc-byl.3", "ag-x31t"}); err != nil {
|
||||
@@ -195,7 +195,7 @@ func TestProvenanceList_FiltersByFromIDAndRelation(t *testing.T) {
|
||||
// Filter by relation.
|
||||
resetProvListFlags()
|
||||
provListJSON = true
|
||||
provListRelation = "decision_produces_artifact"
|
||||
provListRelation = "wasGeneratedBy"
|
||||
c4, out4 := provTestCmd()
|
||||
if err := runProvenanceList(c4, nil); err != nil {
|
||||
t.Fatalf("list rel: %v", err)
|
||||
@@ -204,7 +204,7 @@ func TestProvenanceList_FiltersByFromIDAndRelation(t *testing.T) {
|
||||
if err := json.Unmarshal(out4.Bytes(), &byRel); err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
if len(byRel) != 1 || byRel[0].Relation != "decision_produces_artifact" {
|
||||
if len(byRel) != 1 || byRel[0].Relation != "wasGeneratedBy" {
|
||||
t.Fatalf("relation filter wrong: %+v", byRel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ func seedLedger(t *testing.T) {
|
||||
store := provenancegraph.NewStore(resolveLedgerPath())
|
||||
edges := []provenancegraph.Edge{
|
||||
{FromID: "ag-2", FromType: "decision", ToID: "b.go", ToType: "artifact",
|
||||
Relation: "decision_produces_artifact", TrustTier: "authored", TS: "2026-05-31T02:00:00Z"},
|
||||
Relation: "wasGeneratedBy", TrustTier: "authored", TS: "2026-05-31T02:00:00Z"},
|
||||
{FromID: "ag-1", FromType: "decision", ToID: "a.go", ToType: "artifact",
|
||||
Relation: "decision_produces_artifact", TrustTier: "authored", TS: "2026-05-31T01:00:00Z"},
|
||||
Relation: "wasGeneratedBy", TrustTier: "authored", TS: "2026-05-31T01:00:00Z"},
|
||||
{FromID: "ag-3", FromType: "decision", ToID: "c.go", ToType: "artifact",
|
||||
Relation: "decision_produces_artifact", TrustTier: "authored", TS: "2026-05-31T03:00:00Z"},
|
||||
Relation: "wasGeneratedBy", TrustTier: "authored", TS: "2026-05-31T03:00:00Z"},
|
||||
}
|
||||
for i, e := range edges {
|
||||
if _, err := store.Append(e); err != nil {
|
||||
|
||||
@@ -100,7 +100,7 @@ func beadEdgeLine(t *testing.T, bead string) string {
|
||||
"from_type": "commit",
|
||||
"to_id": bead,
|
||||
"to_type": "bead",
|
||||
"relation": "commit_implements_decision",
|
||||
"relation": "wasRevisionOf",
|
||||
"evidence_ref": "deadbeef",
|
||||
"trust_tier": "inferred",
|
||||
"ts": "2026-05-31T03:00:00Z",
|
||||
|
||||
@@ -3931,7 +3931,7 @@ ao provenance add <from-id> <to-id> [flags]
|
||||
--from-type string Source node type (decision|artifact|bead|...) (default "decision")
|
||||
-h, --help help for add
|
||||
--json Emit the sealed edge as JSON
|
||||
--relation string Typed relation (required), e.g. decision_produces_artifact
|
||||
--relation string Typed PROV-O relation (required), e.g. wasGeneratedBy
|
||||
--to-type string Target node type (decision|artifact|bead|...) (default "artifact")
|
||||
--trust-tier string Trust tier (authored|inferred|mined) (default "authored")
|
||||
--ts string Override the UTC RFC3339 timestamp (defaults to now)
|
||||
|
||||
@@ -62,7 +62,7 @@ func fixturePayloads() ([]LedgerEvent, MapBlobStore) {
|
||||
SchemaVersion: "agentops-sdlc-provenance.v1",
|
||||
FromID: "ag-lmdx", FromType: "bead",
|
||||
ToID: "ag-lmdx.1", ToType: "decision",
|
||||
Relation: "bead_scopes_decision", TrustTier: "authored",
|
||||
Relation: "wasInfluencedBy", TrustTier: "authored",
|
||||
TS: "2026-05-30T00:00:00Z",
|
||||
EvidenceRef: "ag-lmdx",
|
||||
},
|
||||
@@ -70,7 +70,7 @@ func fixturePayloads() ([]LedgerEvent, MapBlobStore) {
|
||||
SchemaVersion: "agentops-sdlc-provenance.v1",
|
||||
FromID: "ag-lmdx.1", FromType: "decision",
|
||||
ToID: "schemas/agentops-sdlc-provenance.v1.schema.json", ToType: "artifact",
|
||||
Relation: "decision_produces_artifact", TrustTier: "authored",
|
||||
Relation: "wasGeneratedBy", TrustTier: "authored",
|
||||
TS: "2026-05-30T01:00:00Z",
|
||||
EvidenceRef: GitBlobOID([]byte(artifactBlob)),
|
||||
},
|
||||
@@ -78,7 +78,7 @@ func fixturePayloads() ([]LedgerEvent, MapBlobStore) {
|
||||
SchemaVersion: "agentops-sdlc-provenance.v1",
|
||||
FromID: "commit:004fcbc2", FromType: "commit",
|
||||
ToID: "ag-lmdx.1", ToType: "decision",
|
||||
Relation: "commit_implements_decision", TrustTier: "inferred",
|
||||
Relation: "wasRevisionOf", TrustTier: "inferred",
|
||||
TS: "2026-05-30T02:00:00Z",
|
||||
EvidenceRef: GitBlobOID([]byte(decisionBlob)),
|
||||
},
|
||||
@@ -130,7 +130,7 @@ func TestRebuild_MatchesOriginal(t *testing.T) {
|
||||
// Content-addressed evidence must be resolved verbatim into the projection.
|
||||
var found bool
|
||||
for _, e := range rebuilt.Edges {
|
||||
if e.Relation == "decision_produces_artifact" {
|
||||
if e.Relation == "wasGeneratedBy" {
|
||||
found = true
|
||||
if e.Evidence != artifactBlob {
|
||||
t.Fatalf("resolved evidence = %q, want %q", e.Evidence, artifactBlob)
|
||||
@@ -138,7 +138,7 @@ func TestRebuild_MatchesOriginal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("decision_produces_artifact edge not present in rebuilt graph")
|
||||
t.Fatal("wasGeneratedBy edge not present in rebuilt graph")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ func TestVerifyChain_DetectsTamper(t *testing.T) {
|
||||
|
||||
tampered := append([]LedgerEvent(nil), chained...)
|
||||
tampered[1].TrustTier = "authored" // was authored already; change to a real downgrade
|
||||
tampered[1].Relation = "verdict_attests_artifact"
|
||||
tampered[1].Relation = "wasAttributedTo"
|
||||
|
||||
if err := VerifyChain(tampered); err == nil {
|
||||
t.Fatal("expected chain verification to detect the tampered relation, got nil")
|
||||
|
||||
@@ -1 +1 @@
|
||||
05c727ee6ca89861f0ee721f931e3c2013d084822130bb60b790571aec102652
|
||||
c0bfa16ee14754713800e80c6b9e667d1976f3644d1652ed8f357e972eae1970
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx","from_type":"bead","to_id":"ag-lmdx.1","to_type":"decision","relation":"bead_scopes_decision","evidence_ref":"ag-lmdx","trust_tier":"authored","ts":"2026-05-30T00:00:00Z","prev_hash":"","payload_hash":"f1ee978ed0e4e9ab094686acb826a11e23b27c2b6eeabfdd0a4bd835a9e82678","hash":"1f1077a80322b2e5d57639b8b4951452608c190cac919237eba4348d7863cc7f"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx.1","from_type":"decision","to_id":"schemas/agentops-sdlc-provenance.v1.schema.json","to_type":"artifact","relation":"decision_produces_artifact","evidence_ref":"dc303301bf7a2eda99f0684dbe8d5185affe8616","trust_tier":"authored","ts":"2026-05-30T01:00:00Z","prev_hash":"1f1077a80322b2e5d57639b8b4951452608c190cac919237eba4348d7863cc7f","payload_hash":"2e132068324098fd024ef167e03afdace38c9549305b94eb86469d88e5ef3159","hash":"edc48a985e4326d5fc40296ecff79aed97d956912f3a7b85c3af25d57436d281"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"commit:004fcbc2","from_type":"commit","to_id":"ag-lmdx.1","to_type":"decision","relation":"commit_implements_decision","evidence_ref":"50125bd74a80bde24b79281d14cdab334971f411","trust_tier":"inferred","ts":"2026-05-30T02:00:00Z","prev_hash":"edc48a985e4326d5fc40296ecff79aed97d956912f3a7b85c3af25d57436d281","payload_hash":"dc79bef70cb5edfe86e588a0ecd8ac29050e1d65da08a8959f34f52a65ded158","hash":"5316584a057e0583c958e9e297679fec3154d076cb12ab9e8801645562fd08a2"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx","from_type":"bead","to_id":"ag-lmdx.1","to_type":"decision","relation":"wasInfluencedBy","evidence_ref":"ag-lmdx","trust_tier":"authored","ts":"2026-05-30T00:00:00Z","prev_hash":"","payload_hash":"a3debf078fde8e00a5acfe210b8c61a93a8ecaee0c7df6f62229c47e5ccfb3d2","hash":"6a1689a30384cfd087da9d4d50adaa90664eb8971948037355e11a7050c5a0a2"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx.1","from_type":"decision","to_id":"schemas/agentops-sdlc-provenance.v1.schema.json","to_type":"artifact","relation":"wasGeneratedBy","evidence_ref":"dc303301bf7a2eda99f0684dbe8d5185affe8616","trust_tier":"authored","ts":"2026-05-30T01:00:00Z","prev_hash":"6a1689a30384cfd087da9d4d50adaa90664eb8971948037355e11a7050c5a0a2","payload_hash":"7675d5909f1ed580bf24374fea66cb0de6fc8290c8eb77a9a00222da9e97bbc1","hash":"d10c49091b0b273fa86f364401ace36b2e1e2725ab33c2a80a42e32f2b8e989e"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"commit:004fcbc2","from_type":"commit","to_id":"ag-lmdx.1","to_type":"decision","relation":"wasRevisionOf","evidence_ref":"50125bd74a80bde24b79281d14cdab334971f411","trust_tier":"inferred","ts":"2026-05-30T02:00:00Z","prev_hash":"d10c49091b0b273fa86f364401ace36b2e1e2725ab33c2a80a42e32f2b8e989e","payload_hash":"0f2c336f20dc83261eac5712a858c0df2dc092c6ff170e0b89aaa884f461af83","hash":"1e0ab2cb58c957ce9306c5a78506636130b21c82e927e6d86c92173bbd2fa5fc"}
|
||||
|
||||
@@ -19,21 +19,21 @@ func fixtureRows() []DoltRow {
|
||||
SchemaVersion: "agentops-sdlc-provenance.v1",
|
||||
FromID: "commit:abc123", FromType: "commit",
|
||||
ToID: "ag-lmdx.3", ToType: "bead",
|
||||
Relation: "commit_implements_decision", EvidenceRef: "ci://run/9",
|
||||
Relation: "wasRevisionOf", EvidenceRef: "ci://run/9",
|
||||
TrustTier: "inferred", TS: "2026-05-30T02:00:00Z",
|
||||
},
|
||||
{
|
||||
SchemaVersion: "agentops-sdlc-provenance.v1",
|
||||
FromID: "ag-lmdx", FromType: "bead",
|
||||
ToID: "ag-lmdx.3", ToType: "decision",
|
||||
Relation: "bead_scopes_decision", EvidenceRef: "ag-lmdx",
|
||||
Relation: "wasInfluencedBy", EvidenceRef: "ag-lmdx",
|
||||
TrustTier: "authored", TS: "2026-05-30T00:00:00Z",
|
||||
},
|
||||
{
|
||||
SchemaVersion: "agentops-sdlc-provenance.v1",
|
||||
FromID: "ag-lmdx.3", FromType: "decision",
|
||||
ToID: "scripts/witness-dolt-jsonl-crosscheck.sh", ToType: "artifact",
|
||||
Relation: "decision_produces_artifact", EvidenceRef: "GOALS.md",
|
||||
Relation: "wasGeneratedBy", EvidenceRef: "GOALS.md",
|
||||
TrustTier: "authored", TS: "2026-05-30T01:00:00Z",
|
||||
},
|
||||
}
|
||||
@@ -167,7 +167,7 @@ func TestCrossCheck_ExtraDoltRowFails(t *testing.T) {
|
||||
SchemaVersion: "agentops-sdlc-provenance.v1",
|
||||
FromID: "ag-lmdx.3", FromType: "decision",
|
||||
ToID: "verdict:green", ToType: "verdict",
|
||||
Relation: "decision_produces_artifact", EvidenceRef: "ci://run/10",
|
||||
Relation: "wasGeneratedBy", EvidenceRef: "ci://run/10",
|
||||
TrustTier: "inferred", TS: "2026-05-30T03:00:00Z",
|
||||
})
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ func provEdge(t *testing.T, from, to string) provenancegraph.Edge {
|
||||
FromType: "commit",
|
||||
ToID: to,
|
||||
ToType: "bead",
|
||||
Relation: "commit_implements_decision",
|
||||
Relation: "wasRevisionOf",
|
||||
EvidenceRef: "deadbeef",
|
||||
TrustTier: "inferred",
|
||||
TS: "2026-05-31T03:00:00Z",
|
||||
|
||||
@@ -14,7 +14,7 @@ func edgeAt(ts, fromID, toID string) Edge {
|
||||
FromType: "decision",
|
||||
ToID: toID,
|
||||
ToType: "artifact",
|
||||
Relation: "decision_produces_artifact",
|
||||
Relation: "wasGeneratedBy",
|
||||
TrustTier: "authored",
|
||||
TS: ts,
|
||||
}
|
||||
|
||||
@@ -40,16 +40,27 @@ var NodeTypes = []string{
|
||||
"pr", "ci_run", "verdict", "learning", "agent",
|
||||
}
|
||||
|
||||
// Relations is the closed set of typed provenance relations from the v1 schema.
|
||||
// Relations is the closed set of typed provenance relations from the v1
|
||||
// schema, named with the W3C PROV-O / PROV-DM standard vocabulary so an
|
||||
// external auditor recognizes the term (ag-lmdx.7). The enum enforces the
|
||||
// standard verbs: a colloquial value such as "derives_from" or the prior
|
||||
// AgentOps-local "artifact_derived_from" is rejected in favor of the PROV-O
|
||||
// term "wasDerivedFrom". Mapping from the prior vocabulary:
|
||||
// decision_produces_artifact->wasGeneratedBy, decision_authorizes->
|
||||
// wasAssociatedWith, artifact_derived_from->wasDerivedFrom,
|
||||
// scenario_covers_artifact->wasInformedBy, verdict_attests_artifact->
|
||||
// wasAttributedTo, bead_scopes_decision->wasInfluencedBy,
|
||||
// commit_implements_decision->wasRevisionOf, learning_revises_decision->
|
||||
// wasInvalidatedBy.
|
||||
var Relations = []string{
|
||||
"decision_produces_artifact",
|
||||
"decision_authorizes",
|
||||
"artifact_derived_from",
|
||||
"scenario_covers_artifact",
|
||||
"verdict_attests_artifact",
|
||||
"bead_scopes_decision",
|
||||
"commit_implements_decision",
|
||||
"learning_revises_decision",
|
||||
"wasGeneratedBy",
|
||||
"wasAssociatedWith",
|
||||
"wasDerivedFrom",
|
||||
"wasInformedBy",
|
||||
"wasAttributedTo",
|
||||
"wasInfluencedBy",
|
||||
"wasRevisionOf",
|
||||
"wasInvalidatedBy",
|
||||
}
|
||||
|
||||
// TrustTiers is the closed, monotonic set of trust tiers (authored > inferred
|
||||
|
||||
@@ -14,7 +14,7 @@ func validEdge() Edge {
|
||||
FromType: "decision",
|
||||
ToID: "cli/cmd/ao/provenance_add.go",
|
||||
ToType: "artifact",
|
||||
Relation: "decision_produces_artifact",
|
||||
Relation: "wasGeneratedBy",
|
||||
TrustTier: "authored",
|
||||
TS: "2026-05-31T00:00:00Z",
|
||||
}
|
||||
|
||||
@@ -95,16 +95,16 @@
|
||||
"relation": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"decision_produces_artifact",
|
||||
"decision_authorizes",
|
||||
"artifact_derived_from",
|
||||
"scenario_covers_artifact",
|
||||
"verdict_attests_artifact",
|
||||
"bead_scopes_decision",
|
||||
"commit_implements_decision",
|
||||
"learning_revises_decision"
|
||||
"wasGeneratedBy",
|
||||
"wasAssociatedWith",
|
||||
"wasDerivedFrom",
|
||||
"wasInformedBy",
|
||||
"wasAttributedTo",
|
||||
"wasInfluencedBy",
|
||||
"wasRevisionOf",
|
||||
"wasInvalidatedBy"
|
||||
],
|
||||
"description": "Typed provenance relation. PROV-DM-aligned: produces≈wasGeneratedBy, authorizes≈wasAuthorizedBy, derived_from≈wasDerivedFrom, attests≈wasAttributedTo."
|
||||
"description": "Typed provenance relation, named with the W3C PROV-O / PROV-DM standard vocabulary so an external auditor recognizes the term (ag-lmdx.7). The enum ENFORCES the standard verbs: a colloquial value such as 'derives_from' or the prior AgentOps-local 'artifact_derived_from' is rejected in favor of the PROV-O term 'wasDerivedFrom'. Edge direction is from->to. Mapping from the prior AgentOps-local vocabulary: decision_produces_artifact->wasGeneratedBy (artifact wasGeneratedBy the decision activity), decision_authorizes->wasAssociatedWith, artifact_derived_from->wasDerivedFrom, scenario_covers_artifact->wasInformedBy, verdict_attests_artifact->wasAttributedTo, bead_scopes_decision->wasInfluencedBy, commit_implements_decision->wasRevisionOf, learning_revises_decision->wasInvalidatedBy. Guard-read/queryable fields (from_id, to_id, relation, trust_tier, and the hash-chain anchors) are first-class top-level columns of each ledger record, never buried inside a nested JSON payload, because Dolt JSON-path generated-column indexing is unreliable; any Dolt projection of this ledger MUST materialize these as real columns rather than indexing JSON paths."
|
||||
},
|
||||
"sha256_hex": {
|
||||
"type": "string",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"from_type": "decision",
|
||||
"to_id": "schemas/agentops-sdlc-provenance.v1.schema.json",
|
||||
"to_type": "artifact",
|
||||
"relation": "decision_produces_artifact",
|
||||
"relation": "wasGeneratedBy",
|
||||
"evidence_ref": ".agents/council/2026-05-30-debate-provenance-substrate.md",
|
||||
"ts": "2026-05-31T00:00:00Z",
|
||||
"prev_hash": "",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"from_type": "decision",
|
||||
"to_id": "schemas/agentops-sdlc-provenance.v1.schema.json",
|
||||
"to_type": "artifact",
|
||||
"relation": "decision_produces_artifact",
|
||||
"relation": "wasGeneratedBy",
|
||||
"evidence_ref": ".agents/council/2026-05-30-debate-provenance-substrate.md",
|
||||
"trust_tier": "authored",
|
||||
"ts": "2026-05-31T00:00:00Z",
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx","from_type":"bead","to_id":"ag-lmdx.3","to_type":"decision","relation":"bead_scopes_decision","evidence_ref":"ag-lmdx","trust_tier":"authored","ts":"2026-05-30T00:00:00Z","prev_hash":"","payload_hash":"e68069a29e8681da1044903ff209f1d435da75433d49ef9de0c38341ba965725","hash":"0074276b4a0c52a73a66552674981f171675541a291284993d2d33757bf37384"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx.3","from_type":"decision","to_id":"scripts/witness-dolt-jsonl-crosscheck.sh","to_type":"artifact","relation":"decision_produces_artifact","evidence_ref":"GOALS.md","trust_tier":"authored","ts":"2026-05-30T01:00:00Z","prev_hash":"0074276b4a0c52a73a66552674981f171675541a291284993d2d33757bf37384","payload_hash":"37e87088c7fa1b15e080379bd82b59eff7eb146fdf1284d2278f58c5d33958dd","hash":"47a0bea1e62d3a51c80cc16bcb184d07dc6b140df842ee8651337f5cbf7f604c"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"commit:abc123","from_type":"commit","to_id":"ag-lmdx.3","to_type":"bead","relation":"commit_implements_decision","evidence_ref":"ci://run/9","trust_tier":"inferred","ts":"2026-05-30T02:00:00Z","prev_hash":"47a0bea1e62d3a51c80cc16bcb184d07dc6b140df842ee8651337f5cbf7f604c","payload_hash":"e45916d808547d8273ff7855e55dd0f70a18fbcb3a6751c8fe1f33828273e7cb","hash":"9b778a6834be1105a31ab840bde224d4e4d696295b1642617a0e658688da69ab"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx","from_type":"bead","to_id":"ag-lmdx.3","to_type":"decision","relation":"wasInfluencedBy","evidence_ref":"ag-lmdx","trust_tier":"authored","ts":"2026-05-30T00:00:00Z","prev_hash":"","payload_hash":"ec83f4f1d4bc25166bd6aff2f8a60e4c4d3679396030c7d7a9eaa7ad12511e16","hash":"89cc38e84e4253fa92dad3c54740f75eedbf357c1815c27669b3c5e343bea1ef"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx.3","from_type":"decision","to_id":"scripts/witness-dolt-jsonl-crosscheck.sh","to_type":"artifact","relation":"wasGeneratedBy","evidence_ref":"GOALS.md","trust_tier":"authored","ts":"2026-05-30T01:00:00Z","prev_hash":"89cc38e84e4253fa92dad3c54740f75eedbf357c1815c27669b3c5e343bea1ef","payload_hash":"327292a1ae5e3089a53d5bf5911985af14edc09004f01c7b30bff59fd0035f7f","hash":"225843bd295d038e10064a8d1bec7c4a94e887d86350809b22b4a847e3302adc"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"commit:abc123","from_type":"commit","to_id":"ag-lmdx.3","to_type":"bead","relation":"wasRevisionOf","evidence_ref":"ci://run/9","trust_tier":"inferred","ts":"2026-05-30T02:00:00Z","prev_hash":"225843bd295d038e10064a8d1bec7c4a94e887d86350809b22b4a847e3302adc","payload_hash":"4fcecd0505dd8c1e2f9d058435be48d871b097238bdb648f7597b8ae502e32cb","hash":"c0352defe8e267cb402e6e984f687695c1dcd384a8f0211f872ae4cf41dbecc2"}
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"commit:abc123","from_type":"commit","to_id":"ag-lmdx.3","to_type":"bead","relation":"commit_implements_decision","evidence_ref":"ci://run/9","trust_tier":"inferred","ts":"2026-05-30T02:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx","from_type":"bead","to_id":"ag-lmdx.3","to_type":"decision","relation":"bead_scopes_decision","evidence_ref":"ag-lmdx","trust_tier":"authored","ts":"2026-05-30T00:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx.3","from_type":"decision","to_id":"scripts/witness-dolt-jsonl-crosscheck.sh","to_type":"artifact","relation":"decision_produces_artifact","evidence_ref":"GOALS.md","trust_tier":"authored","ts":"2026-05-30T01:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"commit:abc123","from_type":"commit","to_id":"ag-lmdx.3","to_type":"bead","relation":"wasRevisionOf","evidence_ref":"ci://run/9","trust_tier":"inferred","ts":"2026-05-30T02:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx","from_type":"bead","to_id":"ag-lmdx.3","to_type":"decision","relation":"wasInfluencedBy","evidence_ref":"ag-lmdx","trust_tier":"authored","ts":"2026-05-30T00:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx.3","from_type":"decision","to_id":"scripts/witness-dolt-jsonl-crosscheck.sh","to_type":"artifact","relation":"wasGeneratedBy","evidence_ref":"GOALS.md","trust_tier":"authored","ts":"2026-05-30T01:00:00Z"}
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"commit:abc123","from_type":"commit","to_id":"ag-lmdx.3","to_type":"bead","relation":"commit_implements_decision","evidence_ref":"ci://run/9","trust_tier":"inferred","ts":"2026-05-30T02:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx","from_type":"bead","to_id":"ag-lmdx.3","to_type":"decision","relation":"bead_scopes_decision","evidence_ref":"ag-lmdx","trust_tier":"mined","ts":"2026-05-30T00:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx.3","from_type":"decision","to_id":"scripts/witness-dolt-jsonl-crosscheck.sh","to_type":"artifact","relation":"decision_produces_artifact","evidence_ref":"GOALS.md","trust_tier":"authored","ts":"2026-05-30T01:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"commit:abc123","from_type":"commit","to_id":"ag-lmdx.3","to_type":"bead","relation":"wasRevisionOf","evidence_ref":"ci://run/9","trust_tier":"inferred","ts":"2026-05-30T02:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx","from_type":"bead","to_id":"ag-lmdx.3","to_type":"decision","relation":"wasInfluencedBy","evidence_ref":"ag-lmdx","trust_tier":"mined","ts":"2026-05-30T00:00:00Z"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-lmdx.3","from_type":"decision","to_id":"scripts/witness-dolt-jsonl-crosscheck.sh","to_type":"artifact","relation":"wasGeneratedBy","evidence_ref":"GOALS.md","trust_tier":"authored","ts":"2026-05-30T01:00:00Z"}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
# Acceptance surface for ag-x31t.2: the provenance ledger event contract +
|
||||
# schema (schemas/agentops-sdlc-provenance.v1.schema.json). The validator
|
||||
# enforces the committed JSON Schema over the tracked fixtures — a
|
||||
# decision_produces_artifact edge VALIDATES, and an otherwise-identical edge
|
||||
# missing trust_tier is REJECTED (the bead's named pass/fail case). Each event
|
||||
# wasGeneratedBy (PROV-O) edge VALIDATES, the prior AgentOps-local
|
||||
# decision_produces_artifact / colloquial derives_from terms are REJECTED, and
|
||||
# an otherwise-identical edge missing trust_tier is REJECTED (the bead's named
|
||||
# pass/fail case + ag-lmdx.7 PROV-O vocabulary enforcement). Each event
|
||||
# is one line of the hash-chained audit ledger at docs/provenance/ledger.jsonl;
|
||||
# the committed ledger is the audit authority (council 2026-05-30).
|
||||
|
||||
@@ -40,8 +42,13 @@ setup() {
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "decision_produces_artifact is a valid relation" {
|
||||
run python3 -c "import json,sys; s=json.load(open('$SCHEMA')); sys.exit(0 if 'decision_produces_artifact' in s['\$defs']['relation']['enum'] else 1)"
|
||||
@test "relation enum uses W3C PROV-O vocabulary (wasGeneratedBy/wasDerivedFrom/wasAttributedTo)" {
|
||||
run python3 -c "import json,sys; s=json.load(open('$SCHEMA')); enum=set(s['\$defs']['relation']['enum']); sys.exit(0 if {'wasGeneratedBy','wasDerivedFrom','wasAttributedTo'} <= enum else 1)"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "the prior AgentOps-local relation vocabulary is rejected (PROV-O enforced)" {
|
||||
run python3 -c "import json,sys; s=json.load(open('$SCHEMA')); enum=set(s['\$defs']['relation']['enum']); legacy={'decision_produces_artifact','artifact_derived_from','derives_from'}; sys.exit(0 if not (legacy & enum) else 1)"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@@ -58,12 +65,22 @@ setup() {
|
||||
[[ "$output" == *"PASS: invalid-missing-trust-tier.json"* ]]
|
||||
}
|
||||
|
||||
@test "decision_produces_artifact edge validates" {
|
||||
@test "a wasGeneratedBy (PROV-O) edge validates" {
|
||||
if [ "$HAVE_JSONSCHEMA" -eq 0 ]; then skip "python3 jsonschema unavailable"; fi
|
||||
run "$SCRIPT" "$FIX/valid-decision-produces-artifact.json"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "an edge using the prior non-PROV-O relation vocabulary is rejected" {
|
||||
if [ "$HAVE_JSONSCHEMA" -eq 0 ]; then skip "python3 jsonschema unavailable"; fi
|
||||
tmp="$BATS_TEST_TMPDIR/legacy-relation.json"
|
||||
cat > "$tmp" <<'JSON'
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-x31t.2","from_type":"decision","to_id":"schemas/x.json","to_type":"artifact","relation":"derives_from","trust_tier":"authored","ts":"2026-05-31T00:00:00Z","prev_hash":"","payload_hash":"cd137deeb225f94ee884b3703485a0effd56937b57f191a10981cc3cc4d4dcee","hash":"0e7224e46af26b3e7b35cfcad9b4bc9838629ae084e48fe024045ea51a5c9ada"}
|
||||
JSON
|
||||
run "$SCRIPT" "$tmp"
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "an edge missing trust_tier is rejected" {
|
||||
if [ "$HAVE_JSONSCHEMA" -eq 0 ]; then skip "python3 jsonschema unavailable"; fi
|
||||
run "$SCRIPT" "$FIX/invalid-missing-trust-tier.json"
|
||||
@@ -74,7 +91,7 @@ setup() {
|
||||
if [ "$HAVE_JSONSCHEMA" -eq 0 ]; then skip "python3 jsonschema unavailable"; fi
|
||||
tmp="$BATS_TEST_TMPDIR/badtier.json"
|
||||
cat > "$tmp" <<'JSON'
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-x31t.2","from_type":"decision","to_id":"schemas/x.json","to_type":"artifact","relation":"decision_produces_artifact","trust_tier":"guessed","ts":"2026-05-31T00:00:00Z","prev_hash":"","payload_hash":"cd137deeb225f94ee884b3703485a0effd56937b57f191a10981cc3cc4d4dcee","hash":"0e7224e46af26b3e7b35cfcad9b4bc9838629ae084e48fe024045ea51a5c9ada"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-x31t.2","from_type":"decision","to_id":"schemas/x.json","to_type":"artifact","relation":"wasGeneratedBy","trust_tier":"guessed","ts":"2026-05-31T00:00:00Z","prev_hash":"","payload_hash":"cd137deeb225f94ee884b3703485a0effd56937b57f191a10981cc3cc4d4dcee","hash":"0e7224e46af26b3e7b35cfcad9b4bc9838629ae084e48fe024045ea51a5c9ada"}
|
||||
JSON
|
||||
run "$SCRIPT" "$tmp"
|
||||
[ "$status" -ne 0 ]
|
||||
@@ -84,7 +101,7 @@ JSON
|
||||
if [ "$HAVE_JSONSCHEMA" -eq 0 ]; then skip "python3 jsonschema unavailable"; fi
|
||||
tmp="$BATS_TEST_TMPDIR/extra.json"
|
||||
cat > "$tmp" <<'JSON'
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-x31t.2","from_type":"decision","to_id":"schemas/x.json","to_type":"artifact","relation":"decision_produces_artifact","trust_tier":"authored","ts":"2026-05-31T00:00:00Z","prev_hash":"","payload_hash":"cd137deeb225f94ee884b3703485a0effd56937b57f191a10981cc3cc4d4dcee","hash":"0e7224e46af26b3e7b35cfcad9b4bc9838629ae084e48fe024045ea51a5c9ada","smuggled":"x"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-x31t.2","from_type":"decision","to_id":"schemas/x.json","to_type":"artifact","relation":"wasGeneratedBy","trust_tier":"authored","ts":"2026-05-31T00:00:00Z","prev_hash":"","payload_hash":"cd137deeb225f94ee884b3703485a0effd56937b57f191a10981cc3cc4d4dcee","hash":"0e7224e46af26b3e7b35cfcad9b4bc9838629ae084e48fe024045ea51a5c9ada","smuggled":"x"}
|
||||
JSON
|
||||
run "$SCRIPT" "$tmp"
|
||||
[ "$status" -ne 0 ]
|
||||
@@ -94,7 +111,7 @@ JSON
|
||||
if [ "$HAVE_JSONSCHEMA" -eq 0 ]; then skip "python3 jsonschema unavailable"; fi
|
||||
tmp="$BATS_TEST_TMPDIR/badhash.json"
|
||||
cat > "$tmp" <<'JSON'
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-x31t.2","from_type":"decision","to_id":"schemas/x.json","to_type":"artifact","relation":"decision_produces_artifact","trust_tier":"authored","ts":"2026-05-31T00:00:00Z","prev_hash":"","payload_hash":"not-a-sha","hash":"0e7224e46af26b3e7b35cfcad9b4bc9838629ae084e48fe024045ea51a5c9ada"}
|
||||
{"schema_version":"agentops-sdlc-provenance.v1","from_id":"ag-x31t.2","from_type":"decision","to_id":"schemas/x.json","to_type":"artifact","relation":"wasGeneratedBy","trust_tier":"authored","ts":"2026-05-31T00:00:00Z","prev_hash":"","payload_hash":"not-a-sha","hash":"0e7224e46af26b3e7b35cfcad9b4bc9838629ae084e48fe024045ea51a5c9ada"}
|
||||
JSON
|
||||
run "$SCRIPT" "$tmp"
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
Reference in New Issue
Block a user