Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/policies/narrative_ci/determinism.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package narrative_ci.determinism

default allow = false

deny_keys := {"ts", "timestamp", "created_at", "updated_at", "time", "datetime"}

allow {
not has_bad_key(input.payload)
}

has_bad_key(x) {
is_object(x)
some k
x[k]
deny_keys[k]
}

has_bad_key(x) {
is_object(x)
some k
has_bad_key(x[k])
}

has_bad_key(x) {
is_array(x)
some i
has_bad_key(x[i])
}

is_object(x) { type_name(x) == "object" }
is_array(x) { type_name(x) == "array" }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "payload": { "ok": 1, "ts": "2026-02-07T00:00:00Z" } }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "payload": { "ok": 1, "note": "no timestamps here" } }
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"allowed_tiers": ["fringe", "mainstream"],
"handoff_candidates": [{ "to_tier": "govt_cert" }]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"allowed_tiers": ["fringe", "mainstream", "govt_cert"],
"handoff_candidates": [{ "to_tier": "govt_cert" }]
}
4 changes: 4 additions & 0 deletions .github/policies/narrative_ci/fixtures/traceability_fail.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"inferred_nodes": [{ "type": "Claim", "id": "clm_missing_receipt" }],
"receipts": []
}
9 changes: 9 additions & 0 deletions .github/policies/narrative_ci/fixtures/traceability_pass.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"inferred_nodes": [{ "type": "Claim", "id": "clm_ok" }],
"receipts": [
{
"target": { "type": "Claim", "id": "clm_ok" },
"sources": [{ "artifact_id": "art_x", "content_sha256": "sha256:abc" }]
}
]
}
19 changes: 19 additions & 0 deletions .github/policies/narrative_ci/tier_taxonomy.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package narrative_ci.tier_taxonomy

default allow = false

allow {
allowed := { t | t := input.allowed_tiers[_] }
every_candidate_tier_allowed(allowed)
}

every_candidate_tier_allowed(allowed) {
cands := input.handoff_candidates
not exists_disallowed(cands, allowed)
}

exists_disallowed(cands, allowed) {
some i
t := cands[i].to_tier
not allowed[t]
}
28 changes: 28 additions & 0 deletions .github/policies/narrative_ci/traceability.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package narrative_ci.traceability

default allow = false

allow {
every_inferred_has_receipt
every_receipt_has_artifact
}

every_inferred_has_receipt {
inferred := input.inferred_nodes
receipts := { r.target.id | r := input.receipts[_] }
not exists_missing(inferred, receipts)
}

exists_missing(inferred, receipts) {
some i
inferred[i].id != ""
not receipts[inferred[i].id]
}

every_receipt_has_artifact {
some r
r := input.receipts[_]
count(r.sources) > 0
r.sources[0].artifact_id != ""
r.sources[0].content_sha256 != ""
}
Comment on lines +22 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The every_receipt_has_artifact rule in .github/policies/narrative_ci/traceability.rego is implemented using an existential ('some') check instead of a universal ('every') check. This logic error means the policy will pass if at least one receipt has an artifact, even if others have no sources, leading to traceability gaps. This defeats the purpose of the traceability gate, which aims to ensure all governed outputs have complete provenance. Furthermore, the current implementation could fail if no receipts are present, even when none are required. The rule should be rewritten to ensure all receipts are properly validated.

every_receipt_has_artifact {
  not exists_receipt_without_artifact
}

exists_receipt_without_artifact {
  some r
  r := input.receipts[_]
  not receipt_has_artifact(r)
}

receipt_has_artifact(r) {
  count(r.sources) > 0
  r.sources[0].artifact_id != ""
  r.sources[0].content_sha256 != ""
}

55 changes: 55 additions & 0 deletions .github/workflows/narrative-ci-verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: narrative-ci-verify

on:
pull_request:
paths:
- "intelgraph/schema/**"
- "intelgraph/pipelines/narrative_ci/**"
- "schemas/narrative/**"
- "fixtures/feb07_2026/**"
- ".github/policies/narrative_ci/**"
- ".github/workflows/narrative-ci-verify.yml"
workflow_dispatch: {}

jobs:
verify:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- uses: actions/checkout@v4

- name: Install OPA
run: |
set -euo pipefail
curl -sSfL -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static
chmod +x opa
sudo mv opa /usr/local/bin/opa

- name: OPA fixtures
run: |
set -euo pipefail
opa eval -d .github/policies/narrative_ci/traceability.rego -i .github/policies/narrative_ci/fixtures/traceability_pass.json "data.narrative_ci.traceability.allow" | rg "true"
! opa eval -d .github/policies/narrative_ci/traceability.rego -i .github/policies/narrative_ci/fixtures/traceability_fail.json "data.narrative_ci.traceability.allow" | rg "true"

opa eval -d .github/policies/narrative_ci/determinism.rego -i .github/policies/narrative_ci/fixtures/determinism_pass.json "data.narrative_ci.determinism.allow" | rg "true"
! opa eval -d .github/policies/narrative_ci/determinism.rego -i .github/policies/narrative_ci/fixtures/determinism_fail.json "data.narrative_ci.determinism.allow" | rg "true"

opa eval -d .github/policies/narrative_ci/tier_taxonomy.rego -i .github/policies/narrative_ci/fixtures/tier_taxonomy_pass.json "data.narrative_ci.tier_taxonomy.allow" | rg "true"
! opa eval -d .github/policies/narrative_ci/tier_taxonomy.rego -i .github/policies/narrative_ci/fixtures/tier_taxonomy_fail.json "data.narrative_ci.tier_taxonomy.allow" | rg "true"

- name: Determinism linter (out/**)
run: |
set -euo pipefail
if [ -d out ]; then
! rg -n "\"(ts|timestamp|created_at|updated_at|datetime|time)\"" out || (echo "timestamp-like keys found in deterministic outputs" && exit 1)
fi

- name: Upload fixtures + policies (debug)
uses: actions/upload-artifact@v4
with:
name: narrative-ci-fixtures
path: |
fixtures/feb07_2026/**
.github/policies/narrative_ci/**
12 changes: 9 additions & 3 deletions docs/roadmap/STATUS.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
{
"last_updated": "2026-02-07T00:00:00Z",
"revision_note": "Added Summit PR Stack Sequencer skill scaffolding.",
"last_updated": "2026-02-07T12:00:00Z",
"revision_note": "Added narrative CI lane-1 fixtures + gates initiative.",
"initiatives": [
{
"id": "adenhq-hive-subsumption-lane1",
"status": "in_progress",
"owner": "codex",
"notes": "Scaffold adenhq/hive subsumption bundle, required check mapping, and evidence-first lane-1 posture."
},
{
"id": "narrative-ci-lane1-fixtures",
"status": "in_progress",
"owner": "codex",
"notes": "Narrative CI contracts, fixtures, deterministic pipeline steps, and OPA gates for Feb 7, 2026 update."
},
{
"id": "B",
"name": "Federation + Ingestion Mesh",
Expand Down Expand Up @@ -200,7 +206,7 @@
"partial": 2,
"incomplete": 0,
"not_started": 5,
"total": 17,
"total": 18,
"ga_blockers": []
}
}
4 changes: 4 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-DELTA-001/metrics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-DELTA-001",
"metrics": { "fixture": true }
}
6 changes: 6 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-DELTA-001/report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-DELTA-001",
"subject": { "type": "narrative_ci_fixture", "name": "SITUPDATE-2026-02-07" },
"result": "pass",
"artifacts": []
}
3 changes: 3 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-DELTA-001/stamp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ts_utc": "2026-02-07T00:00:00Z"
}
4 changes: 4 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-GATES-001/metrics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-GATES-001",
"metrics": { "fixture": true }
}
6 changes: 6 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-GATES-001/report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-GATES-001",
"subject": { "type": "narrative_ci_fixture", "name": "SITUPDATE-2026-02-07" },
"result": "pass",
"artifacts": []
}
3 changes: 3 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-GATES-001/stamp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ts_utc": "2026-02-07T00:00:00Z"
}
4 changes: 4 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-HANDOFF-001/metrics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-HANDOFF-001",
"metrics": { "fixture": true }
}
6 changes: 6 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-HANDOFF-001/report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-HANDOFF-001",
"subject": { "type": "narrative_ci_fixture", "name": "SITUPDATE-2026-02-07" },
"result": "pass",
"artifacts": []
}
3 changes: 3 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-HANDOFF-001/stamp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ts_utc": "2026-02-07T00:00:00Z"
}
4 changes: 4 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-STATE-001/metrics.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-STATE-001",
"metrics": { "fixture": true }
}
6 changes: 6 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-STATE-001/report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-STATE-001",
"subject": { "type": "narrative_ci_fixture", "name": "SITUPDATE-2026-02-07" },
"result": "pass",
"artifacts": []
}
3 changes: 3 additions & 0 deletions evidence/EVD-SITUPDATE-2026-02-07-STATE-001/stamp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ts_utc": "2026-02-07T00:00:00Z"
}
34 changes: 33 additions & 1 deletion evidence/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,38 @@
"metrics": "evidence/EVD-INTSUM-2026-THREAT-HORIZON-001/metrics.json",
"stamp": "evidence/EVD-INTSUM-2026-THREAT-HORIZON-001/stamp.json"
}
},
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-DELTA-001",
"files": {
"report": "evidence/EVD-SITUPDATE-2026-02-07-DELTA-001/report.json",
"metrics": "evidence/EVD-SITUPDATE-2026-02-07-DELTA-001/metrics.json",
"stamp": "evidence/EVD-SITUPDATE-2026-02-07-DELTA-001/stamp.json"
}
},
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-HANDOFF-001",
"files": {
"report": "evidence/EVD-SITUPDATE-2026-02-07-HANDOFF-001/report.json",
"metrics": "evidence/EVD-SITUPDATE-2026-02-07-HANDOFF-001/metrics.json",
"stamp": "evidence/EVD-SITUPDATE-2026-02-07-HANDOFF-001/stamp.json"
}
},
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-STATE-001",
"files": {
"report": "evidence/EVD-SITUPDATE-2026-02-07-STATE-001/report.json",
"metrics": "evidence/EVD-SITUPDATE-2026-02-07-STATE-001/metrics.json",
"stamp": "evidence/EVD-SITUPDATE-2026-02-07-STATE-001/stamp.json"
}
},
{
"evidence_id": "EVD-SITUPDATE-2026-02-07-GATES-001",
"files": {
"report": "evidence/EVD-SITUPDATE-2026-02-07-GATES-001/report.json",
"metrics": "evidence/EVD-SITUPDATE-2026-02-07-GATES-001/metrics.json",
"stamp": "evidence/EVD-SITUPDATE-2026-02-07-GATES-001/stamp.json"
}
}
]
}
}
41 changes: 41 additions & 0 deletions fixtures/feb07_2026/artifact_index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"version": 1,
"artifacts": [
{
"artifact_id": "art_reuters_alekseyev_2026_02_07",
"kind": "news_report",
"uri": "https://www.reuters.com/world/two-suspects-attempted-killing-russian-general-will-soon-be-interrogated-2026-02-07/",
"content_sha256": "sha256:REPLACE_WITH_REAL_HASH_IF_AVAILABLE",
"outlet": "Reuters",
"outlet_tier": "mainstream",
"lang": "en"
},
{
"artifact_id": "art_guardian_alekseyev_2026_02_06",
"kind": "news_report",
"uri": "https://www.theguardian.com/world/2026/feb/06/russia-military-general-vladimir-alekseyev",
"content_sha256": "sha256:REPLACE_WITH_REAL_HASH_IF_AVAILABLE",
"outlet": "The Guardian",
"outlet_tier": "mainstream",
"lang": "en"
},
{
"artifact_id": "art_unit42_shadow_campaigns",
"kind": "vendor_intel",
"uri": "https://unit42.paloaltonetworks.com/shadow-campaigns-uncovering-global-espionage/",
"content_sha256": "sha256:REPLACE_WITH_REAL_HASH_IF_AVAILABLE",
"outlet": "Palo Alto Unit 42",
"outlet_tier": "vendor_intel",
"lang": "en"
},
{
"artifact_id": "art_bleepingcomputer_signal_germany",
"kind": "security_news",
"uri": "https://www.bleepingcomputer.com/news/security/germany-warns-of-signal-account-hijacking-targeting-senior-figures/",
"content_sha256": "sha256:REPLACE_WITH_REAL_HASH_IF_AVAILABLE",
"outlet": "BleepingComputer",
"outlet_tier": "mainstream",
"lang": "en"
}
]
}
47 changes: 47 additions & 0 deletions fixtures/feb07_2026/current_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"version": 1,
"run_id": "run_0002",
"narratives": [
{
"narrative_id": "narr_alekseyev_hit",
"title": "Alekseyev assassination attempt",
"state": "Contested"
},
{
"narrative_id": "narr_shadow_campaigns",
"title": "Shadow Campaigns global espionage",
"state": "Normalized"
},
{
"narrative_id": "narr_signal_phishing",
"title": "Signal account hijacking via fake support",
"state": "Seeded"
}
],
"claims": [
{
"claim_id": "clm_alekseyev_regained_consciousness",
"claim_hash": "hash:cur_regained_consciousness",
"text_norm": "Alekseyev regained consciousness after surgery following the assassination attempt.",
"verifiability": 0.7,
"confidence": 0.6,
"supporting_artifact_ids": ["art_reuters_alekseyev_2026_02_07"]
},
{
"claim_id": "clm_shadow_campaigns_37_155",
"claim_hash": "hash:cur_shadow_37_155",
"text_norm": "Shadow Campaigns compromised targets across 37 countries and conducted reconnaissance across 155 countries.",
"verifiability": 0.9,
"confidence": 0.8,
"supporting_artifact_ids": ["art_unit42_shadow_campaigns"]
},
{
"claim_id": "clm_signal_hijacking_fake_support",
"claim_hash": "hash:cur_signal_fake_support",
"text_norm": "Targets are being phished via fake Signal support chats to hijack Signal accounts.",
"verifiability": 0.85,
"confidence": 0.75,
"supporting_artifact_ids": ["art_bleepingcomputer_signal_germany"]
}
]
}
Loading
Loading