@logicsrc/schemas 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/fixtures/agent.yaml +20 -0
- package/fixtures/agentad-ad.yaml +33 -0
- package/fixtures/agentad-placement.yaml +21 -0
- package/fixtures/opencontext/conformance.json +279 -0
- package/fixtures/opencontext/invalid/audit-event-missing-actor.json +5 -0
- package/fixtures/opencontext/invalid/audit-event-unknown-event.json +9 -0
- package/fixtures/opencontext/invalid/bundle-bad-digest.json +11 -0
- package/fixtures/opencontext/invalid/bundle-bad-id.json +11 -0
- package/fixtures/opencontext/invalid/bundle-missing-digest.json +10 -0
- package/fixtures/opencontext/invalid/bundle-unknown-exclusion-reason.json +17 -0
- package/fixtures/opencontext/invalid/decision-bad-status.json +7 -0
- package/fixtures/opencontext/invalid/decision-missing-decision.json +5 -0
- package/fixtures/opencontext/invalid/decision-wrong-type.json +6 -0
- package/fixtures/opencontext/invalid/diagnostic-missing-ok.json +4 -0
- package/fixtures/opencontext/invalid/diagnostic-unknown-code.json +11 -0
- package/fixtures/opencontext/invalid/manifest-bad-authority.json +10 -0
- package/fixtures/opencontext/invalid/manifest-bad-extension.json +9 -0
- package/fixtures/opencontext/invalid/manifest-bad-id.json +4 -0
- package/fixtures/opencontext/invalid/manifest-bad-ttl.json +7 -0
- package/fixtures/opencontext/invalid/manifest-missing-version.json +3 -0
- package/fixtures/opencontext/invalid/manifest-role-bad-pattern.json +11 -0
- package/fixtures/opencontext/invalid/manifest-unknown-key.json +5 -0
- package/fixtures/opencontext/invalid/object-bad-digest.json +10 -0
- package/fixtures/opencontext/invalid/object-bad-id.json +4 -0
- package/fixtures/opencontext/invalid/object-bad-layer.json +5 -0
- package/fixtures/opencontext/invalid/object-bad-redaction-mode.json +10 -0
- package/fixtures/opencontext/invalid/object-bad-supersedes.json +7 -0
- package/fixtures/opencontext/invalid/object-confidence-out-of-range.json +5 -0
- package/fixtures/opencontext/invalid/object-extra-property.json +5 -0
- package/fixtures/opencontext/invalid/object-missing-id.json +3 -0
- package/fixtures/opencontext/invalid/object-missing-type.json +3 -0
- package/fixtures/opencontext/invalid/object-source-without-uri.json +9 -0
- package/fixtures/opencontext/invalid/object-unknown-authority.json +5 -0
- package/fixtures/opencontext/invalid/object-unknown-classification.json +5 -0
- package/fixtures/opencontext/invalid/object-unknown-trust.json +5 -0
- package/fixtures/opencontext/invalid/provenance-canonical-false.json +4 -0
- package/fixtures/opencontext/invalid/provenance-no-source.json +3 -0
- package/fixtures/opencontext/invalid/role-bad-classification.json +4 -0
- package/fixtures/opencontext/invalid/role-bad-include.json +6 -0
- package/fixtures/opencontext/invalid-manifest.json +177 -0
- package/fixtures/opencontext/resolution/authority-conflict/context/policies/refunds-observed.md +10 -0
- package/fixtures/opencontext/resolution/authority-conflict/context/policies/refunds.md +12 -0
- package/fixtures/opencontext/resolution/authority-conflict/expected.json +21 -0
- package/fixtures/opencontext/resolution/authority-conflict/opencontext.yaml +11 -0
- package/fixtures/opencontext/resolution/classification-ceiling/context/docs/handbook.md +11 -0
- package/fixtures/opencontext/resolution/classification-ceiling/context/docs/litigation.md +11 -0
- package/fixtures/opencontext/resolution/classification-ceiling/expected.json +32 -0
- package/fixtures/opencontext/resolution/classification-ceiling/opencontext.yaml +16 -0
- package/fixtures/opencontext/resolution/deny-overrides-allow/context/mission.md +10 -0
- package/fixtures/opencontext/resolution/deny-overrides-allow/context/policies/internal/margins.md +10 -0
- package/fixtures/opencontext/resolution/deny-overrides-allow/context/policies/refunds.md +10 -0
- package/fixtures/opencontext/resolution/deny-overrides-allow/expected.json +19 -0
- package/fixtures/opencontext/resolution/deny-overrides-allow/opencontext.yaml +17 -0
- package/fixtures/opencontext/resolution/duplicate-canonical/context/policies/refunds-rewrite.md +12 -0
- package/fixtures/opencontext/resolution/duplicate-canonical/context/policies/refunds.md +12 -0
- package/fixtures/opencontext/resolution/duplicate-canonical/expected.json +10 -0
- package/fixtures/opencontext/resolution/duplicate-canonical/opencontext.yaml +11 -0
- package/fixtures/opencontext/resolution/lifecycle/context/notes/current.md +11 -0
- package/fixtures/opencontext/resolution/lifecycle/context/notes/expired.md +12 -0
- package/fixtures/opencontext/resolution/lifecycle/context/notes/future.md +12 -0
- package/fixtures/opencontext/resolution/lifecycle/context/notes/stale.md +11 -0
- package/fixtures/opencontext/resolution/lifecycle/expected.json +30 -0
- package/fixtures/opencontext/resolution/lifecycle/opencontext.yaml +14 -0
- package/fixtures/opencontext/resolution/object-permissions/context/policies/payroll.md +12 -0
- package/fixtures/opencontext/resolution/object-permissions/context/policies/refunds.md +10 -0
- package/fixtures/opencontext/resolution/object-permissions/expected.json +32 -0
- package/fixtures/opencontext/resolution/object-permissions/opencontext.yaml +14 -0
- package/fixtures/opencontext/resolution/redaction/context/customers/acme.json +21 -0
- package/fixtures/opencontext/resolution/redaction/expected.json +28 -0
- package/fixtures/opencontext/resolution/redaction/opencontext.yaml +19 -0
- package/fixtures/opencontext/resolution/supersession/context/pricing/enterprise.v1.md +12 -0
- package/fixtures/opencontext/resolution/supersession/context/pricing/enterprise.v2.md +14 -0
- package/fixtures/opencontext/resolution/supersession/expected.json +33 -0
- package/fixtures/opencontext/resolution/supersession/opencontext.yaml +11 -0
- package/fixtures/opencontext/valid/audit-event.json +26 -0
- package/fixtures/opencontext/valid/bundle.json +67 -0
- package/fixtures/opencontext/valid/decision.json +51 -0
- package/fixtures/opencontext/valid/diagnostic.json +37 -0
- package/fixtures/opencontext/valid/manifest-minimal.json +4 -0
- package/fixtures/opencontext/valid/manifest.json +134 -0
- package/fixtures/opencontext/valid/object-minimal.json +4 -0
- package/fixtures/opencontext/valid/object-policy.json +75 -0
- package/fixtures/opencontext/valid/object-structured.json +34 -0
- package/fixtures/opencontext/valid/object-untrusted.json +19 -0
- package/fixtures/opencontext/valid/provenance-canonical.json +4 -0
- package/fixtures/opencontext/valid/provenance.json +14 -0
- package/fixtures/opencontext/valid/role.json +38 -0
- package/fixtures/openontology/conformance.json +158 -0
- package/fixtures/openontology/invalid/action-no-permissions.json +13 -0
- package/fixtures/openontology/invalid/changeset-empty-operations.json +10 -0
- package/fixtures/openontology/invalid/changeset-unknown-op.json +15 -0
- package/fixtures/openontology/invalid/claim-both-objects.json +14 -0
- package/fixtures/openontology/invalid/claim-confidence-out-of-range.json +14 -0
- package/fixtures/openontology/invalid/claim-extra-property.json +14 -0
- package/fixtures/openontology/invalid/claim-unknown-status.json +13 -0
- package/fixtures/openontology/invalid/entity-missing-canonical-name.json +8 -0
- package/fixtures/openontology/invalid/evidence-unknown-selector.json +10 -0
- package/fixtures/openontology/invalid/manifest-bad-id.json +15 -0
- package/fixtures/openontology/invalid/manifest-bad-version.json +15 -0
- package/fixtures/openontology/invalid/manifest-no-maintainer.json +11 -0
- package/fixtures/openontology/invalid/relationship-no-range.json +11 -0
- package/fixtures/openontology/valid/action.json +29 -0
- package/fixtures/openontology/valid/approval.json +12 -0
- package/fixtures/openontology/valid/changeset.json +34 -0
- package/fixtures/openontology/valid/claim-relationship.json +23 -0
- package/fixtures/openontology/valid/claim-value.json +15 -0
- package/fixtures/openontology/valid/constraint.json +12 -0
- package/fixtures/openontology/valid/entity-type.json +16 -0
- package/fixtures/openontology/valid/entity.json +16 -0
- package/fixtures/openontology/valid/event.json +17 -0
- package/fixtures/openontology/valid/evidence.json +12 -0
- package/fixtures/openontology/valid/manifest.json +21 -0
- package/fixtures/openontology/valid/namespace.json +7 -0
- package/fixtures/openontology/valid/property.json +8 -0
- package/fixtures/openontology/valid/query-adhoc.json +16 -0
- package/fixtures/openontology/valid/query.json +24 -0
- package/fixtures/openontology/valid/relationship-type.json +16 -0
- package/fixtures/openontology/valid/review.json +17 -0
- package/fixtures/openontology/valid/source.json +13 -0
- package/fixtures/openprd/conformance.json +104 -0
- package/fixtures/openprd/invalid/bad-filename.md +41 -0
- package/fixtures/openprd/invalid/bad-id-format.md +41 -0
- package/fixtures/openprd/invalid/bad-status.md +41 -0
- package/fixtures/openprd/invalid/dates-backwards.md +43 -0
- package/fixtures/openprd/invalid/duplicate-requirement.md +42 -0
- package/fixtures/openprd/invalid/id-mismatch.md +41 -0
- package/fixtures/openprd/invalid/missing-section.md +37 -0
- package/fixtures/openprd/invalid/missing-title.md +40 -0
- package/fixtures/openprd/invalid/no-front-matter.md +33 -0
- package/fixtures/openprd/invalid/out-of-order.md +41 -0
- package/fixtures/openprd/invalid/superseded-without-replacement.md +41 -0
- package/fixtures/openprd/invalid/unknown-key.md +42 -0
- package/fixtures/openprd/valid/bold-requirements.md +41 -0
- package/fixtures/openprd/valid/full.md +48 -0
- package/fixtures/openprd/valid/minimal.md +41 -0
- package/fixtures/openprd/valid/none-sections.md +41 -0
- package/fixtures/openprd/valid/subsections.md +47 -0
- package/fixtures/openprd/valid/superseded.md +42 -0
- package/fixtures/openprd-prd.yaml +12 -0
- package/fixtures/pull-request.yaml +24 -0
- package/fixtures/repo.yaml +27 -0
- package/fixtures/task.yaml +26 -0
- package/package.json +91 -0
- package/schemas/agentad-ad-request.schema.json +32 -0
- package/schemas/agentad-ad-response.schema.json +31 -0
- package/schemas/agentad-ad.schema.json +79 -0
- package/schemas/agentad-campaign.schema.json +40 -0
- package/schemas/agentad-click.schema.json +23 -0
- package/schemas/agentad-impression.schema.json +18 -0
- package/schemas/agentad-placement.schema.json +46 -0
- package/schemas/logicsrc-account-audit-event.schema.json +31 -0
- package/schemas/logicsrc-account-grant.schema.json +48 -0
- package/schemas/logicsrc-account-provider.schema.json +31 -0
- package/schemas/logicsrc-agent.schema.json +51 -0
- package/schemas/logicsrc-connected-account.schema.json +39 -0
- package/schemas/logicsrc-credential-audit-event.schema.json +32 -0
- package/schemas/logicsrc-credential-provider.schema.json +28 -0
- package/schemas/logicsrc-credential-sync-plan.schema.json +56 -0
- package/schemas/logicsrc-credential-sync-run.schema.json +35 -0
- package/schemas/logicsrc-email-message.schema.json +22 -0
- package/schemas/logicsrc-event.schema.json +25 -0
- package/schemas/logicsrc-opencontext-audit-event.schema.json +69 -0
- package/schemas/logicsrc-opencontext-bundle.schema.json +210 -0
- package/schemas/logicsrc-opencontext-decision.schema.json +229 -0
- package/schemas/logicsrc-opencontext-diagnostic.schema.json +123 -0
- package/schemas/logicsrc-opencontext-manifest.schema.json +337 -0
- package/schemas/logicsrc-opencontext-object.schema.json +311 -0
- package/schemas/logicsrc-opencontext-provenance.schema.json +76 -0
- package/schemas/logicsrc-opencontext-role.schema.json +93 -0
- package/schemas/logicsrc-openontology-action.schema.json +90 -0
- package/schemas/logicsrc-openontology-approval.schema.json +39 -0
- package/schemas/logicsrc-openontology-changeset.schema.json +168 -0
- package/schemas/logicsrc-openontology-claim.schema.json +148 -0
- package/schemas/logicsrc-openontology-constraint.schema.json +104 -0
- package/schemas/logicsrc-openontology-entity-type.schema.json +107 -0
- package/schemas/logicsrc-openontology-entity.schema.json +59 -0
- package/schemas/logicsrc-openontology-event.schema.json +60 -0
- package/schemas/logicsrc-openontology-evidence.schema.json +121 -0
- package/schemas/logicsrc-openontology-manifest.schema.json +148 -0
- package/schemas/logicsrc-openontology-namespace.schema.json +21 -0
- package/schemas/logicsrc-openontology-package.schema.json +73 -0
- package/schemas/logicsrc-openontology-property.schema.json +83 -0
- package/schemas/logicsrc-openontology-query.schema.json +207 -0
- package/schemas/logicsrc-openontology-relationship-type.schema.json +57 -0
- package/schemas/logicsrc-openontology-review.schema.json +37 -0
- package/schemas/logicsrc-openontology-source.schema.json +43 -0
- package/schemas/logicsrc-plugin.schema.json +35 -0
- package/schemas/logicsrc-pull-request.schema.json +73 -0
- package/schemas/logicsrc-repo.schema.json +73 -0
- package/schemas/logicsrc-run.schema.json +69 -0
- package/schemas/logicsrc-social-post.schema.json +26 -0
- package/schemas/logicsrc-task.schema.json +114 -0
- package/schemas/openprd-prd.schema.json +79 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://logicsrc.com/schemas/opencontext/bundle.schema.json",
|
|
4
|
+
"title": "OpenContext Context Bundle",
|
|
5
|
+
"description": "The portable output of resolution: the authorized, valid, current subset of context selected for one consumer and one task, with its provenance and a deterministic digest. JSON is the canonical interchange form. A bundle is the unit that makes agents replaceable — the same bundle handed to a different model or runtime carries the same organizational knowledge, and a decision can record exactly which context produced it by citing the digest.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["opencontext", "bundle_id", "generated_at", "consumer", "objects", "digest"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"opencontext": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^\\d+\\.\\d+(\\.\\d+)?$",
|
|
13
|
+
"description": "Specification version the bundle conforms to."
|
|
14
|
+
},
|
|
15
|
+
"bundle_id": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"pattern": "^ocb_[a-z0-9_-]+$",
|
|
18
|
+
"description": "Identifier for this bundle, prefixed ocb_. Derived from the digest by the reference implementation so identical inputs produce an identical id."
|
|
19
|
+
},
|
|
20
|
+
"generated_at": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"format": "date-time",
|
|
23
|
+
"description": "When the bundle was compiled. Excluded from the digest so that two runs over unchanged sources are byte-identical apart from this field."
|
|
24
|
+
},
|
|
25
|
+
"namespace": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
|
28
|
+
"description": "The manifest id the bundle was resolved from."
|
|
29
|
+
},
|
|
30
|
+
"consumer": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["type", "id"],
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"description": "Who this bundle was resolved for. Recorded so an audit can answer which agent saw which context.",
|
|
35
|
+
"properties": {
|
|
36
|
+
"type": { "type": "string", "enum": ["agent", "human", "role", "service"] },
|
|
37
|
+
"id": { "type": "string", "minLength": 1 },
|
|
38
|
+
"roles": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"uniqueItems": true,
|
|
41
|
+
"items": { "type": "string", "minLength": 1 },
|
|
42
|
+
"description": "Effective roles applied during authorization."
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"task": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "The task the context was resolved for, verbatim. Used for relevance ranking and recorded for reproducibility."
|
|
49
|
+
},
|
|
50
|
+
"as_of": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"format": "date-time",
|
|
53
|
+
"description": "The instant resolution was evaluated against. Lifecycle state, supersession, and validity windows are all judged at this timestamp, so passing --at reproduces a past view of context."
|
|
54
|
+
},
|
|
55
|
+
"objects": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"description": "The resolved context, in deterministic order: layer, then authority, then id. Content here has already been authorized, filtered, and redacted.",
|
|
58
|
+
"items": { "$ref": "#/$defs/bundledObject" }
|
|
59
|
+
},
|
|
60
|
+
"excluded": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"description": "What was considered and left out, and why. Populated when --explain is requested; a bundle with an empty excluded list is not a claim that nothing was excluded.",
|
|
63
|
+
"items": { "$ref": "#/$defs/exclusion" }
|
|
64
|
+
},
|
|
65
|
+
"warnings": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"description": "Non-fatal findings that survived into the bundle: stale context, unresolved canonical conflicts, missing provenance, untrusted content. Warnings are never silently dropped.",
|
|
68
|
+
"items": { "$ref": "#/$defs/warning" }
|
|
69
|
+
},
|
|
70
|
+
"provenance": {
|
|
71
|
+
"type": "array",
|
|
72
|
+
"description": "Flattened source records for every object in the bundle. Provenance MUST survive compilation, so this list stands on its own even if content was summarised.",
|
|
73
|
+
"items": { "$ref": "#/$defs/provenanceEntry" }
|
|
74
|
+
},
|
|
75
|
+
"permissions": {
|
|
76
|
+
"type": "array",
|
|
77
|
+
"uniqueItems": true,
|
|
78
|
+
"items": { "type": "string", "minLength": 1 },
|
|
79
|
+
"description": "Capability strings the consumer holds, carried through for the runtime to enforce."
|
|
80
|
+
},
|
|
81
|
+
"stats": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"description": "Counters describing the resolution, useful for context budgeting.",
|
|
85
|
+
"properties": {
|
|
86
|
+
"considered": { "type": "integer", "minimum": 0 },
|
|
87
|
+
"included": { "type": "integer", "minimum": 0 },
|
|
88
|
+
"excluded": { "type": "integer", "minimum": 0 },
|
|
89
|
+
"redacted": { "type": "integer", "minimum": 0 },
|
|
90
|
+
"characters": { "type": "integer", "minimum": 0, "description": "Total characters of compiled content. A proxy for token cost that needs no tokenizer." }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"digest": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
96
|
+
"description": "sha256 over the canonical JSON of the bundle with generated_at and digest themselves omitted. Deterministic: identical source state and inputs produce an identical digest, which is what lets a decision record cite exactly the context that produced it."
|
|
97
|
+
},
|
|
98
|
+
"extensions": { "$ref": "#/$defs/extensions" }
|
|
99
|
+
},
|
|
100
|
+
"$defs": {
|
|
101
|
+
"bundledObject": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"required": ["id", "type"],
|
|
104
|
+
"additionalProperties": true,
|
|
105
|
+
"description": "A resolved context object. Carries the object's declared fields plus the state the resolver computed for it.",
|
|
106
|
+
"properties": {
|
|
107
|
+
"id": { "type": "string", "minLength": 1 },
|
|
108
|
+
"type": { "type": "string", "minLength": 1 },
|
|
109
|
+
"layer": { "type": "string", "enum": ["L0", "L1", "L2", "L3", "L4", "L5"] },
|
|
110
|
+
"title": { "type": "string" },
|
|
111
|
+
"content": { "anyOf": [{ "type": "string" }, { "type": "object" }, { "type": "array" }] },
|
|
112
|
+
"content_type": { "type": "string" },
|
|
113
|
+
"authority": { "type": "string", "enum": ["canonical", "approved", "reference", "observed", "inferred", "historical"] },
|
|
114
|
+
"trust": {
|
|
115
|
+
"type": "string",
|
|
116
|
+
"enum": ["trusted", "verified", "untrusted"],
|
|
117
|
+
"description": "Preserved through resolution. An integration MUST be able to tell canonical policy apart from text a stranger wrote into a ticket."
|
|
118
|
+
},
|
|
119
|
+
"owner": { "type": "string" },
|
|
120
|
+
"version": { "type": "integer", "minimum": 1 },
|
|
121
|
+
"updated": { "type": "string", "format": "date-time" },
|
|
122
|
+
"classification": { "type": "string", "enum": ["public", "internal", "confidential", "restricted"] },
|
|
123
|
+
"durability": { "type": "string", "enum": ["ephemeral", "session", "operational", "long-lived", "permanent"] },
|
|
124
|
+
"lifecycle": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"enum": ["future", "current", "stale", "expired", "superseded"],
|
|
127
|
+
"description": "Computed against as_of, not stored on the object."
|
|
128
|
+
},
|
|
129
|
+
"redacted": {
|
|
130
|
+
"type": "array",
|
|
131
|
+
"items": { "type": "string" },
|
|
132
|
+
"description": "Paths removed or masked before compilation. The bundle discloses that redaction happened without disclosing what was redacted."
|
|
133
|
+
},
|
|
134
|
+
"sources": { "type": "array", "items": { "type": "object" } },
|
|
135
|
+
"tags": { "type": "array", "items": { "type": "string" } },
|
|
136
|
+
"extensions": { "$ref": "#/$defs/extensions" }
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"exclusion": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"required": ["id", "reason"],
|
|
142
|
+
"additionalProperties": false,
|
|
143
|
+
"description": "One object that was considered and rejected, with the pipeline stage that rejected it.",
|
|
144
|
+
"properties": {
|
|
145
|
+
"id": { "type": "string", "minLength": 1 },
|
|
146
|
+
"reason": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"enum": [
|
|
149
|
+
"permission-denied",
|
|
150
|
+
"classification-denied",
|
|
151
|
+
"scope-exclusion",
|
|
152
|
+
"not-in-scope",
|
|
153
|
+
"superseded",
|
|
154
|
+
"expired",
|
|
155
|
+
"not-yet-valid",
|
|
156
|
+
"outranked",
|
|
157
|
+
"unapproved",
|
|
158
|
+
"not-relevant",
|
|
159
|
+
"conflict",
|
|
160
|
+
"source-unavailable"
|
|
161
|
+
],
|
|
162
|
+
"description": "Why it was left out. permission-denied and classification-denied are authorization outcomes and are decided before any relevance work is done."
|
|
163
|
+
},
|
|
164
|
+
"detail": { "type": "string", "description": "Human-readable specifics, e.g. the pattern that excluded it or the object that outranked it." },
|
|
165
|
+
"outranked_by": { "type": "string", "description": "Id of the object that won, when reason is outranked or superseded." }
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"warning": {
|
|
169
|
+
"type": "object",
|
|
170
|
+
"required": ["code", "message"],
|
|
171
|
+
"additionalProperties": false,
|
|
172
|
+
"properties": {
|
|
173
|
+
"code": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
|
|
174
|
+
"message": { "type": "string", "minLength": 1 },
|
|
175
|
+
"id": { "type": "string", "description": "Object the warning concerns, when it concerns one." },
|
|
176
|
+
"severity": { "type": "string", "enum": ["info", "warning", "error"], "default": "warning" }
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"provenanceEntry": {
|
|
180
|
+
"type": "object",
|
|
181
|
+
"required": ["id"],
|
|
182
|
+
"additionalProperties": false,
|
|
183
|
+
"description": "Where one bundled object came from.",
|
|
184
|
+
"properties": {
|
|
185
|
+
"id": { "type": "string", "minLength": 1, "description": "The context object id." },
|
|
186
|
+
"canonical_source": { "type": "boolean", "description": "True when the object is itself the origin and has no upstream source." },
|
|
187
|
+
"sources": {
|
|
188
|
+
"type": "array",
|
|
189
|
+
"items": {
|
|
190
|
+
"type": "object",
|
|
191
|
+
"required": ["uri"],
|
|
192
|
+
"additionalProperties": true,
|
|
193
|
+
"properties": {
|
|
194
|
+
"uri": { "type": "string", "minLength": 1 },
|
|
195
|
+
"type": { "type": "string" },
|
|
196
|
+
"retrieved_at": { "type": "string", "format": "date-time" },
|
|
197
|
+
"digest": { "type": "string", "pattern": "^(sha256):[0-9a-f]{64}$" },
|
|
198
|
+
"trust": { "type": "string", "enum": ["trusted", "verified", "untrusted"] }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"extensions": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"propertyNames": { "type": "string", "pattern": "^[a-z0-9]+(\\.[a-z0-9-]+)+$" },
|
|
207
|
+
"additionalProperties": true
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://logicsrc.com/schemas/opencontext/decision.schema.json",
|
|
4
|
+
"title": "OpenContext Decision Record",
|
|
5
|
+
"description": "A context object recording a decision that was made, why, by whom, and on what context. A decision record is what turns a resolution into organizational history: by citing the digest of the Context Bundle it was made from, it stays reproducible after the agent that made it is gone, the model is replaced, and the underlying policies have moved on. Decision records are ordinary context objects with type 'decision', so everything true of an object — authority, permissions, supersession, provenance — is also true here.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["id", "type", "title", "decision"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"id": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[a-z0-9][a-z0-9_-]*(\\.[a-z0-9][a-z0-9_-]*)*$",
|
|
13
|
+
"description": "Stable id. Convention is decision.<date>-<slug>, e.g. decision.2026-08-09-model-provider, which sorts chronologically and never collides."
|
|
14
|
+
},
|
|
15
|
+
"type": { "type": "string", "const": "decision" },
|
|
16
|
+
"layer": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"enum": ["L0", "L1", "L2", "L3", "L4", "L5"],
|
|
19
|
+
"default": "L5",
|
|
20
|
+
"description": "Decisions are usually L5 operational when fresh; a decision that becomes standing policy is better superseded by an L3 policy object than silently re-labelled."
|
|
21
|
+
},
|
|
22
|
+
"title": { "type": "string", "minLength": 1, "description": "What was decided, in one line." },
|
|
23
|
+
"status": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"enum": ["proposed", "accepted", "rejected", "superseded", "deprecated"],
|
|
26
|
+
"description": "Where the decision stands. Only accepted decisions are resolved into bundles by default."
|
|
27
|
+
},
|
|
28
|
+
"decision": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"minLength": 1,
|
|
31
|
+
"description": "The decision itself, stated as a commitment rather than a discussion, e.g. 'Use provider X as the default runtime.'"
|
|
32
|
+
},
|
|
33
|
+
"rationale": {
|
|
34
|
+
"anyOf": [
|
|
35
|
+
{ "type": "string" },
|
|
36
|
+
{ "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
37
|
+
],
|
|
38
|
+
"description": "Why. A list of drivers (latency, cost, reliability) or a paragraph. This is the part that survives usefully once the people who were in the room have moved on."
|
|
39
|
+
},
|
|
40
|
+
"alternatives": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"description": "Options considered and not taken. Recording these stops the same option being relitigated every six months.",
|
|
43
|
+
"items": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"required": ["option"],
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"properties": {
|
|
48
|
+
"option": { "type": "string", "minLength": 1 },
|
|
49
|
+
"rejected_because": { "type": "string" }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"consequences": {
|
|
54
|
+
"anyOf": [
|
|
55
|
+
{ "type": "string" },
|
|
56
|
+
{ "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
57
|
+
],
|
|
58
|
+
"description": "What this commits the organization to, including the costs accepted."
|
|
59
|
+
},
|
|
60
|
+
"authority": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"enum": ["canonical", "approved", "reference", "observed", "inferred", "historical"],
|
|
63
|
+
"description": "Usually approved once a decision is accepted. A decision an agent proposed is inferred until a human approves it."
|
|
64
|
+
},
|
|
65
|
+
"owner": { "type": "string", "minLength": 1, "description": "Role or identity accountable for the decision." },
|
|
66
|
+
"approved_by": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"description": "Who signed off. An accepted decision with no approvals is reported by doctor.",
|
|
69
|
+
"items": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"additionalProperties": false,
|
|
72
|
+
"anyOf": [
|
|
73
|
+
{ "required": ["role"], "properties": { "role": { "type": "string" } } },
|
|
74
|
+
{ "required": ["id"], "properties": { "id": { "type": "string" } } }
|
|
75
|
+
],
|
|
76
|
+
"properties": {
|
|
77
|
+
"role": { "type": "string", "minLength": 1 },
|
|
78
|
+
"id": { "type": "string", "minLength": 1 },
|
|
79
|
+
"at": { "type": "string", "format": "date-time" }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"decided_by": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"additionalProperties": false,
|
|
86
|
+
"description": "The consumer that made the decision — a human, or the agent that proposed it. Recorded so a decision made by an agent is never indistinguishable from one made by a person.",
|
|
87
|
+
"properties": {
|
|
88
|
+
"type": { "type": "string", "enum": ["agent", "human", "role", "service"] },
|
|
89
|
+
"id": { "type": "string", "minLength": 1 }
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"bundle": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"description": "The Context Bundle this decision was made from. Citing the digest makes the decision auditable: a reader can prove which context was and was not in front of the decider.",
|
|
96
|
+
"properties": {
|
|
97
|
+
"bundle_id": { "type": "string", "pattern": "^ocb_[a-z0-9_-]+$" },
|
|
98
|
+
"digest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
|
|
99
|
+
"generated_at": { "type": "string", "format": "date-time" },
|
|
100
|
+
"uri": { "type": "string", "description": "Where the bundle itself was archived, if it was." }
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"created": { "type": "string", "format": "date-time" },
|
|
104
|
+
"updated": { "type": "string", "format": "date-time" },
|
|
105
|
+
"valid_from": { "type": "string", "format": "date-time" },
|
|
106
|
+
"expires": { "anyOf": [{ "type": "string", "format": "date-time" }, { "type": "null" }] },
|
|
107
|
+
"ttl": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"pattern": "^\\d+(ms|s|m|h|d|w|y)$",
|
|
110
|
+
"description": "Staleness window. A decision that has not been revisited inside it is reported stale — decisions rot like any other context."
|
|
111
|
+
},
|
|
112
|
+
"review": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"additionalProperties": false,
|
|
115
|
+
"properties": {
|
|
116
|
+
"interval": { "type": "string", "pattern": "^\\d+(ms|s|m|h|d|w|y)$" },
|
|
117
|
+
"required_approvers": { "type": "integer", "minimum": 1 },
|
|
118
|
+
"next_review": { "type": "string", "format": "date" },
|
|
119
|
+
"last_review": { "type": "string", "format": "date" }
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"applies_to": {
|
|
123
|
+
"type": "array",
|
|
124
|
+
"uniqueItems": true,
|
|
125
|
+
"items": { "type": "string", "minLength": 1 },
|
|
126
|
+
"description": "Roles, agents, products, or scopes this decision governs."
|
|
127
|
+
},
|
|
128
|
+
"depends_on": {
|
|
129
|
+
"type": "array",
|
|
130
|
+
"uniqueItems": true,
|
|
131
|
+
"items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*(\\.[a-z0-9][a-z0-9_-]*)*(@\\d+)?$" },
|
|
132
|
+
"description": "Context that must resolve alongside this decision for it to make sense."
|
|
133
|
+
},
|
|
134
|
+
"conflicts_with": {
|
|
135
|
+
"type": "array",
|
|
136
|
+
"uniqueItems": true,
|
|
137
|
+
"items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*(\\.[a-z0-9][a-z0-9_-]*)*(@\\d+)?$" },
|
|
138
|
+
"description": "Decisions or policies known to contradict this one."
|
|
139
|
+
},
|
|
140
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
141
|
+
"language": { "type": "string" },
|
|
142
|
+
"approval": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"additionalProperties": false,
|
|
145
|
+
"properties": {
|
|
146
|
+
"required": { "type": "boolean" },
|
|
147
|
+
"roles": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } },
|
|
148
|
+
"minimum": { "type": "integer", "minimum": 1 },
|
|
149
|
+
"approved_by": {
|
|
150
|
+
"type": "array",
|
|
151
|
+
"items": {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"additionalProperties": false,
|
|
154
|
+
"properties": {
|
|
155
|
+
"role": { "type": "string", "minLength": 1 },
|
|
156
|
+
"id": { "type": "string", "minLength": 1 },
|
|
157
|
+
"at": { "type": "string", "format": "date-time" }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"redact": {
|
|
164
|
+
"type": "array",
|
|
165
|
+
"items": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"required": ["path"],
|
|
168
|
+
"additionalProperties": false,
|
|
169
|
+
"properties": {
|
|
170
|
+
"path": { "type": "string", "minLength": 1 },
|
|
171
|
+
"mode": { "type": "string", "enum": ["remove", "mask", "hash"] },
|
|
172
|
+
"replacement": { "type": "string" },
|
|
173
|
+
"reason": { "type": "string" }
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"content_uri": { "type": "string", "minLength": 1 },
|
|
178
|
+
"durability": { "type": "string", "enum": ["ephemeral", "session", "operational", "long-lived", "permanent"] },
|
|
179
|
+
"classification": { "type": "string", "enum": ["public", "internal", "confidential", "restricted"] },
|
|
180
|
+
"trust": { "type": "string", "enum": ["trusted", "verified", "untrusted"] },
|
|
181
|
+
"permissions": {
|
|
182
|
+
"type": "object",
|
|
183
|
+
"additionalProperties": false,
|
|
184
|
+
"properties": {
|
|
185
|
+
"read": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } },
|
|
186
|
+
"write": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } },
|
|
187
|
+
"deny": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } }
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"supersedes": {
|
|
191
|
+
"type": "array",
|
|
192
|
+
"uniqueItems": true,
|
|
193
|
+
"items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*(\\.[a-z0-9][a-z0-9_-]*)*(@\\d+)?$" },
|
|
194
|
+
"description": "Earlier decisions this one replaces. Reversing a decision supersedes it; it does not delete it."
|
|
195
|
+
},
|
|
196
|
+
"superseded_by": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*(\\.[a-z0-9][a-z0-9_-]*)*(@\\d+)?$" },
|
|
197
|
+
"references": {
|
|
198
|
+
"type": "array",
|
|
199
|
+
"uniqueItems": true,
|
|
200
|
+
"items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]*(\\.[a-z0-9][a-z0-9_-]*)*(@\\d+)?$" },
|
|
201
|
+
"description": "Context this decision depends on — the policies, products, or prior decisions it was reasoning about."
|
|
202
|
+
},
|
|
203
|
+
"version": { "type": "integer", "minimum": 1 },
|
|
204
|
+
"sources": {
|
|
205
|
+
"type": "array",
|
|
206
|
+
"items": {
|
|
207
|
+
"type": "object",
|
|
208
|
+
"required": ["uri"],
|
|
209
|
+
"additionalProperties": true,
|
|
210
|
+
"properties": {
|
|
211
|
+
"uri": { "type": "string", "minLength": 1 },
|
|
212
|
+
"type": { "type": "string" },
|
|
213
|
+
"retrieved_at": { "type": "string", "format": "date-time" },
|
|
214
|
+
"digest": { "type": "string", "pattern": "^(sha256):[0-9a-f]{64}$" }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"canonical_source": { "type": "boolean" },
|
|
219
|
+
"tags": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } },
|
|
220
|
+
"content": { "anyOf": [{ "type": "string" }, { "type": "object" }, { "type": "array" }] },
|
|
221
|
+
"content_type": { "type": "string" },
|
|
222
|
+
"summary": { "type": "string" },
|
|
223
|
+
"extensions": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"propertyNames": { "type": "string", "pattern": "^[a-z0-9]+(\\.[a-z0-9-]+)+$" },
|
|
226
|
+
"additionalProperties": true
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://logicsrc.com/schemas/opencontext/diagnostic.schema.json",
|
|
4
|
+
"title": "OpenContext Diagnostic Report",
|
|
5
|
+
"description": "The machine-readable output of `opencontext validate` and `opencontext doctor`: every finding, where it came from, how bad it is, and the resulting health score. Diagnostics are the contract CI depends on, so the codes are normative and stable — a pipeline that fails on duplicate-canonical must keep failing on it across implementations and versions.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["opencontext", "ok", "findings"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"opencontext": { "type": "string", "pattern": "^\\d+\\.\\d+(\\.\\d+)?$" },
|
|
11
|
+
"ok": {
|
|
12
|
+
"type": "boolean",
|
|
13
|
+
"description": "True when no finding meets or exceeds the configured failure severity. This is what the exit code follows."
|
|
14
|
+
},
|
|
15
|
+
"generated_at": { "type": "string", "format": "date-time" },
|
|
16
|
+
"namespace": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
17
|
+
"score": {
|
|
18
|
+
"type": "number",
|
|
19
|
+
"minimum": 0,
|
|
20
|
+
"maximum": 100,
|
|
21
|
+
"description": "Context health, 0-100. Computed as 100 minus the sum of weight x affected objects for each finding, normalised by the object count and clamped at 0. The weights are documented and configurable, so a score is comparable only within a repository's own configuration."
|
|
22
|
+
},
|
|
23
|
+
"counts": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"additionalProperties": false,
|
|
26
|
+
"description": "Roll-up used by the human-readable report.",
|
|
27
|
+
"properties": {
|
|
28
|
+
"objects": { "type": "integer", "minimum": 0 },
|
|
29
|
+
"errors": { "type": "integer", "minimum": 0 },
|
|
30
|
+
"warnings": { "type": "integer", "minimum": 0 },
|
|
31
|
+
"info": { "type": "integer", "minimum": 0 },
|
|
32
|
+
"stale": { "type": "integer", "minimum": 0 },
|
|
33
|
+
"expired": { "type": "integer", "minimum": 0 },
|
|
34
|
+
"conflicting": { "type": "integer", "minimum": 0 },
|
|
35
|
+
"orphaned": { "type": "integer", "minimum": 0 },
|
|
36
|
+
"missing_owner": { "type": "integer", "minimum": 0 },
|
|
37
|
+
"broken_sources": { "type": "integer", "minimum": 0 }
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"findings": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"description": "Every finding, ordered most severe first, then by code, then by object id, so two runs over the same repository produce byte-identical reports.",
|
|
43
|
+
"items": { "$ref": "#/$defs/finding" }
|
|
44
|
+
},
|
|
45
|
+
"extensions": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"propertyNames": { "type": "string", "pattern": "^[a-z0-9]+(\\.[a-z0-9-]+)+$" },
|
|
48
|
+
"additionalProperties": true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"$defs": {
|
|
52
|
+
"severity": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"enum": ["info", "warning", "error"],
|
|
55
|
+
"description": "error breaks the contract and fails a strict run; warning is context rot that needs attention but still resolves; info is advisory."
|
|
56
|
+
},
|
|
57
|
+
"finding": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"required": ["code", "severity", "message"],
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"properties": {
|
|
62
|
+
"code": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"enum": [
|
|
65
|
+
"schema-invalid",
|
|
66
|
+
"manifest-invalid",
|
|
67
|
+
"duplicate-id",
|
|
68
|
+
"duplicate-canonical",
|
|
69
|
+
"unknown-authority",
|
|
70
|
+
"conflict-declared",
|
|
71
|
+
"conflict-ambiguous",
|
|
72
|
+
"broken-supersession",
|
|
73
|
+
"supersession-cycle",
|
|
74
|
+
"multiple-active-versions",
|
|
75
|
+
"broken-reference",
|
|
76
|
+
"orphaned",
|
|
77
|
+
"missing-owner",
|
|
78
|
+
"missing-provenance",
|
|
79
|
+
"missing-digest",
|
|
80
|
+
"stale",
|
|
81
|
+
"expired",
|
|
82
|
+
"not-yet-valid",
|
|
83
|
+
"review-overdue",
|
|
84
|
+
"unapproved",
|
|
85
|
+
"unknown-scheme",
|
|
86
|
+
"source-unavailable",
|
|
87
|
+
"path-traversal",
|
|
88
|
+
"invalid-permission",
|
|
89
|
+
"unknown-role",
|
|
90
|
+
"role-cycle",
|
|
91
|
+
"empty-scope",
|
|
92
|
+
"secret-detected",
|
|
93
|
+
"untrusted-canonical",
|
|
94
|
+
"unknown-extension"
|
|
95
|
+
],
|
|
96
|
+
"description": "Stable diagnostic code. duplicate-canonical, conflict-ambiguous, and broken-supersession are the checks that keep the resolver from quietly guessing; secret-detected and path-traversal are security checks; unknown-extension is only ever raised in strict mode."
|
|
97
|
+
},
|
|
98
|
+
"severity": { "$ref": "#/$defs/severity" },
|
|
99
|
+
"message": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"minLength": 1,
|
|
102
|
+
"description": "What is wrong, in a sentence an author can act on."
|
|
103
|
+
},
|
|
104
|
+
"id": { "type": "string", "description": "Context object id the finding concerns." },
|
|
105
|
+
"ids": {
|
|
106
|
+
"type": "array",
|
|
107
|
+
"items": { "type": "string" },
|
|
108
|
+
"description": "Every object involved, when a finding is about a relationship rather than one object — the two canonical policies that collide, or the chain that broke."
|
|
109
|
+
},
|
|
110
|
+
"file": { "type": "string", "description": "Path the object was loaded from, relative to the manifest." },
|
|
111
|
+
"line": { "type": "integer", "minimum": 1, "description": "1-indexed line, when the source format carries positions." },
|
|
112
|
+
"column": { "type": "integer", "minimum": 1 },
|
|
113
|
+
"field": { "type": "string", "description": "Dotted path of the offending field, e.g. permissions.read." },
|
|
114
|
+
"expected": { "description": "What the field should have been." },
|
|
115
|
+
"actual": { "description": "What it was." },
|
|
116
|
+
"remediation": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"description": "The concrete next action, e.g. 'Add owner: support, or set health.require_owner: false'."
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|