Skip to content
Open
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
5 changes: 5 additions & 0 deletions .github/policies/competition/antitrust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: Antitrust Classification
rules:
- id: antitrust-01
description: Flag mentions of anticompetitive conduct
condition: text contains "anti-competitive"
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.

medium

The condition text contains "anti-competitive" is case-sensitive and very specific. It will miss variations like "anticompetitive" or "Anti-competitive". Consider using a more flexible matching strategy if supported by the policy engine.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Broaden the match condition to avoid false negatives.

The current condition only catches the exact hyphenated phrase and will miss common variants like anticompetitive (and likely case variants), so antitrust mentions can slip through.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/policies/competition/antitrust.yml at line 5, The condition
currently only checks for the exact string "anti-competitive"; replace it with a
case-insensitive regex that matches both hyphenated and non-hyphenated forms,
e.g. change the rule from condition: text contains "anti-competitive" to a regex
match like condition: text matches "(?i)\\banti-?competitive\\b" so it catches
"anti-competitive", "anticompetitive", and case variants.

6 changes: 6 additions & 0 deletions docs/ops/runbooks/competition-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Competition Events Runbook

* Alert if:
* extraction fails >5%
* schema mismatch
* SLO: 99% parse success
Comment on lines +4 to +6
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Alert threshold conflicts with stated SLO.

A 99% parse-success SLO means a 1% failure budget, but the runbook alerts only after failures exceed 5%, which is too late for SLO protection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/ops/runbooks/competition-events.md` around lines 4 - 6, The runbook's
alert threshold "extraction fails >5%" conflicts with the declared SLO "SLO: 99%
parse success" (1% failure budget); update the alerting threshold to align with
the SLO (e.g., trigger when extraction fails >1% or a lower value like >0.5% for
early warning) or change the SLO to match the existing alerting (e.g., SLO: 95%
parse success) and make the change in the lines containing "extraction fails
>5%" and "SLO: 99% parse success" so the two values are consistent.

7 changes: 7 additions & 0 deletions docs/security/data-handling/competition-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Competition Events Data Handling

* Public news only
* Never log:
* raw prompts
* internal embeddings
* Retention: 30 days raw, permanent structured
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

“Permanent structured” retention needs a compliance guardrail.

Without an explicit deletion/reevaluation policy for structured records, this creates a data-minimization and retention-risk gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/security/data-handling/competition-events.md` at line 7, The phrase
"Retention: 30 days raw, permanent structured" lacks a compliance
guardrail—update the doc to replace or augment "permanent structured" with an
explicit retention and reevaluation policy: define a structured-record
retention_duration (e.g., X years or indefinite with conditions), a periodic
review_cycle (e.g., annually), responsible_role or team (e.g., Data Protection
Officer / Data Retention Owner), required transformations
(pseudonymization/aggregation) and legal_basis for indefinite storage, and
mention audit/logging and deletion procedures (how to delete or re-evaluate
records and who approves exceptions) so the structured retention has clear
operational and compliance controls.

7 changes: 7 additions & 0 deletions docs/standards/competition-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Competition Events Standards

| Format | Supported |
| ------------- | ------------ |
| JSON | ✅ |
| Graph (Neo4j) | ✅ |
| OpenAI schema | ❌ (non-goal)|
1 change: 1 addition & 0 deletions scripts/bench/competition-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('Benchmark script stub');
1 change: 1 addition & 0 deletions scripts/monitoring/competition-drift.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('Monitoring script stub');
5 changes: 5 additions & 0 deletions src/agents/competition-risk-agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class CompetitionRiskAgent {
analyze(text: string) {
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.

medium

The text parameter is currently unused. Prefixing it with an underscore is a common convention to indicate an intentionally unused parameter in TypeScript.

Suggested change
analyze(text: string) {
analyze(_text: string) {

return { riskScore: 0.8, flags: ['antitrust'] };
Comment on lines +2 to +3
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

analyze currently classifies every text as antitrust risk.

This is functionally incorrect for a production path and will flood downstream systems with false positives.

Proposed minimal baseline behavior
 export class CompetitionRiskAgent {
   analyze(text: string) {
-    return { riskScore: 0.8, flags: ['antitrust'] };
+    const normalized = text.toLowerCase();
+    const antitrustHit = /(anti-competitive|anticompetitive|antitrust|monopoly)/.test(normalized);
+    return {
+      riskScore: antitrustHit ? 0.8 : 0.1,
+      flags: antitrustHit ? ['antitrust'] : [],
+    };
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
analyze(text: string) {
return { riskScore: 0.8, flags: ['antitrust'] };
analyze(text: string) {
const normalized = text.toLowerCase();
const antitrustHit = /(anti-competitive|anticompetitive|antitrust|monopoly)/.test(normalized);
return {
riskScore: antitrustHit ? 0.8 : 0.1,
flags: antitrustHit ? ['antitrust'] : [],
};
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/agents/competition-risk-agent.ts` around lines 2 - 3, The analyze method
currently returns a hardcoded antitrust risk for every input; change
analyze(text: string) in the CompetitionRiskAgent to compute a real heuristic:
scan the input for configurable antitrust keywords/phrases (e.g., "collude",
"price fixing", "bid rigging", "market allocation"), count hits and compute a
normalized riskScore (0.0–1.0) based on hits and text length, and populate flags
only when relevant (e.g., include 'antitrust' or more specific flags like
'price-fixing'); if no keywords are found return riskScore near 0 and an empty
flags array. Ensure the function signature remains analyze(text: string) and add
a simple unit test or comment describing the keyword list so maintainers can
tune thresholds.

}
}
11 changes: 11 additions & 0 deletions src/connectors/rest/regulatoryNews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import crypto from 'node:crypto';

export function extractCompetitionEvent(article: string) {
const hash = crypto.createHash('sha256').update(article).digest('hex');
Comment on lines +3 to +4
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Reject empty/blank articles before hashing.

Creating events from blank input makes ingestion noisy and can produce duplicate placeholder events.

Suggested input validation
 export function extractCompetitionEvent(article: string) {
+  if (!article || article.trim().length === 0) {
+    throw new Error('article must be a non-empty string');
+  }
   const hash = crypto.createHash('sha256').update(article).digest('hex');
   return {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function extractCompetitionEvent(article: string) {
const hash = crypto.createHash('sha256').update(article).digest('hex');
export function extractCompetitionEvent(article: string) {
if (!article || article.trim().length === 0) {
throw new Error('article must be a non-empty string');
}
const hash = crypto.createHash('sha256').update(article).digest('hex');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/connectors/rest/regulatoryNews.ts` around lines 3 - 4, The function
extractCompetitionEvent creates a hash from the article without validating
input; add an early guard in extractCompetitionEvent that checks if article is
null/undefined or article.trim() === '' and reject it (e.g., return null or
throw a ValidationError) before calling crypto.createHash, so blank or
whitespace-only inputs are not hashed or turned into events.

return {
id: hash,
type: "REGULATORY_COMPETITION_EVENT",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Emit schema discriminator as event_type

The extractor returns type, but the newly added CompetitionEvent schema defines event_type as the discriminator. This mismatch means payloads from extractCompetitionEvent do not conform to the declared schema and can be dropped or misclassified by consumers expecting CompetitionEvent-shaped objects.

Useful? React with 👍 / 👎.

actors: [],
Comment on lines +5 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate schema and inspect event field names =="
fd -i 'competitionEvent.ts' | while read -r f; do
  echo "--- $f ---"
  rg -n -C2 'interface\s+CompetitionEvent|event_type|type\s*:' "$f"
done

echo
echo "== Inspect connector payload fields =="
fd -i 'regulatoryNews.ts' | while read -r f; do
  echo "--- $f ---"
  rg -n -C2 'extractCompetitionEvent|event_type|type\s*:' "$f"
done

Repository: BrianCLong/summit

Length of output: 747


Fix field name mismatch: type should be event_type.

The schema defines event_type as the field name (src/graphrag/schema/competitionEvent.ts, line 4), but the connector returns type (src/connectors/rest/regulatoryNews.ts, line 7). This contract violation will break validation and ingestion. Rename the field to event_type to match the schema.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/connectors/rest/regulatoryNews.ts` around lines 5 - 8, The returned
object in the regulatory news connector is using the wrong field name "type"
which must be renamed to "event_type" to match the schema; update the return
value (the object that includes id: hash, type: "REGULATORY_COMPETITION_EVENT",
actors: []) to replace the "type" property with "event_type" (event_type:
"REGULATORY_COMPETITION_EVENT") so that the output of the regulatoryNews
connector conforms to the competition event schema and passes validation.

claims: [],
};
}
7 changes: 7 additions & 0 deletions src/graphrag/schema/competitionEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { EvidenceReport } from '../evidence/types.js';

export interface CompetitionEvent extends EvidenceReport {
event_type: 'REGULATORY_COMPETITION_EVENT';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Use a single discriminant key across schema and extractor (type vs event_type).

CompetitionEvent uses event_type, but the regulatory extractor/test contract uses type. This creates a guaranteed shape mismatch for competition events and can break downstream classification/query logic.

Proposed fix
 export interface CompetitionEvent extends EvidenceReport {
-  event_type: 'REGULATORY_COMPETITION_EVENT';
+  type: 'REGULATORY_COMPETITION_EVENT';
   actors: string[];
   claims: string[];
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/graphrag/schema/competitionEvent.ts` at line 4, The schema uses
event_type but the regulatory extractor/test contract expects the discriminant
key type, causing a shape mismatch; update the CompetitionEvent schema to use
type: 'REGULATORY_COMPETITION_EVENT' (replace event_type) and search/rename any
references to event_type in related code/tests/extractors to use type so the
discriminant key is consistent with the regulatory extractor and downstream
consumers.

actors: string[];
claims: string[];
}
9 changes: 9 additions & 0 deletions subsumption/competition-events/claims.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Claim Registry

| Planned Element | Source |
| --------------------------------- | ------------- |
| Competition-risk detector | ITEM:CLAIM-02 |
| Regulatory event ingestion | ITEM:CLAIM-01 |
| Actor conflict graph edges | ITEM:CLAIM-04 |
| Market competition classification | ITEM:CLAIM-03 |
| Antitrust policy tagging | ITEM:CLAIM-05 |
57 changes: 57 additions & 0 deletions subsumption/competition-events/manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
version: 1
item:
title: "TEMPLATE: Replace with ITEM title"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Replace template placeholders with competition-events bundle values.

This manifest is still wired to _template and subsumption-bundle-framework artifacts, so the competition-events bundle won’t resolve the correct claims/docs/fixtures.

Proposed fix
 item:
-  title: "TEMPLATE: Replace with ITEM title"
+  title: "Competition Events Subsumption Bundle"
@@
-item_slug: "_template"
+item_slug: "competition-events"
@@
 claim_registry:
   mode: "missing-item"
-  claims_file: "subsumption/_template/claims.md"
+  claims_file: "subsumption/competition-events/claims.md"
@@
 prs:
-  - title: "feat(governance): add subsumption bundle framework verifier"
+  - title: "feat(governance): add competition events subsumption bundle"
@@
 docs_targets:
-  - "docs/standards/subsumption-bundle-framework.md"
-  - "docs/security/data-handling/subsumption-bundle-framework.md"
-  - "docs/ops/runbooks/subsumption-bundle-framework.md"
+  - "docs/standards/competition-events.md"
+  - "docs/security/data-handling/competition-events.md"
+  - "docs/ops/runbooks/competition-events.md"
@@
 fixtures:
-  deny_policy_dir: "subsumption/_template/fixtures/policy/deny"
+  deny_policy_dir: "subsumption/competition-events/fixtures/policy/deny"
   tests:
     positive:
-      - "subsumption/_template/fixtures/tests/positive/minimal_bundle"
+      - "subsumption/competition-events/fixtures/tests/positive/minimal_bundle"
     negative:
-      - "subsumption/_template/fixtures/tests/negative/missing_docs"
+      - "subsumption/competition-events/fixtures/tests/negative/missing_docs"

Also applies to: 8-8, 12-12, 15-15, 35-37, 43-48

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@subsumption/competition-events/manifest.yaml` at line 3, The manifest still
contains template placeholders (e.g., title: "TEMPLATE: Replace with ITEM
title") and references to the `_template` and `subsumption-bundle-framework`
artifacts which prevent the competition-events bundle from resolving correct
claims/docs/fixtures; update the manifest.yaml to replace the TEMPLATE
placeholders with the real `competition-events` values and swap any `_template`
and `subsumption-bundle-framework` artifact references to the actual
competition-events artifact names used by this bundle (check fields around
title, any `artifact:` entries, `docs`, `fixtures`, and `claims` keys) so all
references point to the competition-events resources instead of the
template/framework ones.

links: []
type: "unknown"
date_version: "unknown"
intriguing_claim: "TEMPLATE: 1–2 sentences"
item_slug: "_template"
Comment on lines +3 to +8
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.

high

The manifest metadata contains placeholder values from the template. These should be updated to reflect the specific 'Competition Events' bundle.

  title: "Competition Events Subsumption Bundle"
  links: []
  type: "governance"
  date_version: "1.0.0"
  intriguing_claim: "Tracks regulatory competition events and anti-competitive conduct allegations."
item_slug: "competition-events"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Set bundle slug to competition-events

item_slug is still _template, so any tooling that derives identity from the manifest will attribute this bundle’s evidence to the template namespace instead of competition-events (the repo’s verifier extracts slug text and writes it into emitted evidence metadata). That creates collisions with other template-derived bundles and makes this new bundle non-addressable as its own ITEM.

Useful? React with 👍 / 👎.


claim_registry:
mode: "missing-item"
claims_file: "subsumption/_template/claims.md"
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.

high

The claims_file path points to the template directory. It should point to the claims.md file created in this PR.

  claims_file: "subsumption/competition-events/claims.md"


prs:
- title: "feat(governance): add subsumption bundle framework verifier"
lane: "foundation"
owner: "master"
risk: "green"

gates:
- name: "verify-subsumption-bundle"
required_checks_status: "assumed"
deny_by_default_required: true
Comment on lines +22 to +23
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid assumed check status when deny-by-default is required.

Using "assumed" for required checks/discovery weakens enforcement and can allow policy gates to pass without verified CI evidence.

Proposed hardening
 gates:
   - name: "verify-subsumption-bundle"
-    required_checks_status: "assumed"
+    required_checks_status: "required"
     deny_by_default_required: true
@@
 required_checks_discovery:
-  status: "assumed"
+  status: "required"
   instructions: "docs/required_checks.todo.md"

Also applies to: 56-57

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@subsumption/competition-events/manifest.yaml` around lines 22 - 23, The
manifest currently sets required_checks_status: "assumed" while
deny_by_default_required: true which weakens enforcement; change
required_checks_status to a non-assumed value (e.g., "required" or "enforced")
so the deny-by-default gate requires actual CI verification, and apply the same
change for the other occurrence mentioned (lines 56-57) to ensure both places
use the stricter status.


evidence:
schemas:
report: "evidence/schemas/report.schema.json"
metrics: "evidence/schemas/metrics.schema.json"
stamp: "evidence/schemas/stamp.schema.json"
index: "evidence/index.json"
required_ids:
- "EVD-SUBSUMPTION-FRAMEWORK-GOV-001"

docs_targets:
- "docs/standards/subsumption-bundle-framework.md"
- "docs/security/data-handling/subsumption-bundle-framework.md"
- "docs/ops/runbooks/subsumption-bundle-framework.md"
Comment on lines +35 to +37
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.

high

The docs_targets paths point to generic framework documentation. They should be updated to match the documentation files added in this PR.

  - "docs/standards/competition-events.md"
  - "docs/security/data-handling/competition-events.md"
  - "docs/ops/runbooks/competition-events.md"

- "docs/required_checks.todo.md"
- "docs/repo_assumptions.md"
- "docs/decisions/subsumption-bundle-framework.md"

fixtures:
deny_policy_dir: "subsumption/_template/fixtures/policy/deny"
tests:
positive:
- "subsumption/_template/fixtures/tests/positive/minimal_bundle"
negative:
- "subsumption/_template/fixtures/tests/negative/missing_docs"

feature_flags: []
dependency_delta:
required_if_deps_change: true
doc_root: "deps_delta"

required_checks_discovery:
status: "assumed"
instructions: "docs/required_checks.todo.md"
10 changes: 10 additions & 0 deletions tests/connectors/regulatoryNews.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import test from 'node:test';
import assert from 'node:assert';
import { extractCompetitionEvent } from '../../src/connectors/rest/regulatoryNews.js';

test('extractCompetitionEvent', () => {
const article = 'OpenAI formally requested regulatory scrutiny from California AG.';
const event = extractCompetitionEvent(article);
assert.strictEqual(event.type, 'REGULATORY_COMPETITION_EVENT');
assert.ok(event.id);
});