Skip to content
Merged
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
28 changes: 24 additions & 4 deletions .github/workflows/validate-shacl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,29 @@ jobs:

- name: Validate examples with SHACL
run: |
# We check if there are any .ttl files in examples/rdf to validate
if ls examples/rdf/*.ttl 1> /dev/null 2>&1; then
python scripts/validate_rdf.py examples/rdf/*.ttl --shapes shacl/hacp-core.ttl
else
if ! ls examples/rdf/*.ttl 1> /dev/null 2>&1; then
echo "No RDF examples found to validate."
exit 0
fi

positive_examples=(
examples/rdf/irreversible_task.ttl
examples/rdf/low_confidence_escalation.ttl
examples/rdf/multi_agent_handoff.ttl
)
Comment on lines +32 to +36

Choose a reason for hiding this comment

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

P2 Badge Validate all non-negative RDF fixtures in CI

The workflow now validates only a hardcoded subset of positive fixtures, so any new .ttl example added under examples/rdf will be skipped unless someone also edits this list. That regresses the previous wildcard coverage and can let broken example artifacts merge with a green CI run, which undermines the repository’s SHACL-based compliance checks.

Useful? React with 👍 / 👎.

positive_culture_examples=(
examples/rdf/culture_profile.ttl
)
negative_examples=(
examples/rdf/failed_validation.ttl
)

python scripts/validate_rdf.py "${positive_examples[@]}" --shapes shacl/hacp-core.ttl
python scripts/validate_rdf.py "${positive_culture_examples[@]}" --shapes shacl/culture-profile.shacl.ttl

if python scripts/validate_rdf.py "${negative_examples[@]}" --shapes shacl/hacp-core.ttl; then
echo "Expected SHACL failure for negative fixtures, but validation passed."
exit 1
else
echo "Negative fixtures failed core SHACL as expected."
Comment on lines +47 to +51

Choose a reason for hiding this comment

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

P2 Badge Distinguish expected SHACL failures from runtime errors

This if treats any non-zero exit from validate_rdf.py as an expected negative-fixture result, but that script also exits non-zero on parse/path errors (not just SHACL non-conformance). As a result, a missing negative fixture file or other runtime failure will still produce a passing CI message ("failed as expected"), masking real breakage in the validation pipeline.

Useful? React with 👍 / 👎.

Copy link

Choose a reason for hiding this comment

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

Negative test masks script errors as expected failures

Medium Severity

The negative fixture test treats any non-zero exit code from validate_rdf.py as an expected SHACL validation failure. If the script crashes due to a missing file (e.g., failed_validation.ttl gets deleted), an import error, or any other runtime exception, the else branch still runs and the CI reports success with "Negative fixtures failed core SHACL as expected." Since positive examples use different files, their success doesn't guard against this masking. The test needs to distinguish a SHACL validation rejection from a script crash.

Fix in Cursor Fix in Web

fi
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ All notable changes to this project will be documented in this file.
- **Documentation**: `docs/architecture.md`, Decision Matrix in `HACP.md`.
- **Examples**: Irreversible tasks, Escalation scenarios, Validation failures.
- **CI/CD**: `validate_rdf.py` script and GitHub Workflow.
- **Culture**: Added `schemas/culture-profile.yaml` and `shacl/culture-profile.shacl.ttl`.
- **Culture Example**: Added `examples/culture_profile.yaml` and `examples/rdf/culture_profile.ttl`.

### Changed

- Refined `task-manifest.yaml` with collaboration modes and deadlines.
- Clarified refusal and model identity requirements in `HACP.md`.
- Added optional `team_id` and `culture_profile_ref` to `schemas/task-manifest.yaml`.
- Added `Culture Policy Binding` requirement in `HACP.md`.
- Updated core SHACL authority-boundary logic to conditionally require `humanApproval` for irreversible executions.
- Updated CI validation to run positive and negative fixture suites separately.
3 changes: 3 additions & 0 deletions COMPLIANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

See `shacl/*.ttl` — each test is encoded as a SHACL NodeShape. Run `pyshacl` (CI job included) against examples to verify compliance.

- Core controls: `shacl/hacp-core.ttl`
- Culture controls: `shacl/culture-profile.shacl.ttl`

## Compliance Claim Format

Systems claiming compliance should publish a short YAML claim, for example:
Expand Down
5 changes: 5 additions & 0 deletions HACP.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ Every task MUST be classified before execution along these axes:

Unclassified tasks MUST default to **Human-Led, AI-Assisted** handling. Silent delegation is prohibited.

### 3.2 Culture Policy Binding

Teams MAY publish a Culture Profile artifact that defines decision-rights model, dissent channels, and risk policy tiers.
For tasks where `importance=High` OR `reversibility=Irreversible`, collaboration **MUST** apply the matching Culture Profile `risk_policy` tier rules, including `requires_divergence` when that flag is set.
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The specification states that teams MAY publish a Culture Profile, but then requires that tasks with importance=High OR reversibility=Irreversible MUST apply the matching Culture Profile risk_policy tier rules. This creates ambiguity: what should happen when such a task is executed but no Culture Profile exists? Consider clarifying whether: (1) a Culture Profile is required for high-importance/irreversible tasks, (2) there should be a default fallback behavior, or (3) the MUST only applies when a Culture Profile is present.

Suggested change
For tasks where `importance=High` OR `reversibility=Irreversible`, collaboration **MUST** apply the matching Culture Profile `risk_policy` tier rules, including `requires_divergence` when that flag is set.
When a Culture Profile is present, tasks where `importance=High` OR `reversibility=Irreversible` **MUST** apply the matching Culture Profile `risk_policy` tier rules, including `requires_divergence` when that flag is set. If no Culture Profile exists, such tasks MUST still follow a documented, human-governed risk policy and escalation path consistent with this protocol.

Copilot uses AI. Check for mistakes.

## 4. Roles

### 4.1 Human Roles
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ Goal: make the protocol real and executable so that institutions and vendors can
## Quick start

1. Read the normative spec: `HACP.md`
2. Inspect the schema: `schemas/task-manifest.yaml`
3. Validate an example with SHACL (CI job included): See `.github/workflows/validate-shacl.yml`
2. Inspect the schemas: `schemas/task-manifest.yaml` and `schemas/culture-profile.yaml`
3. Validate examples with SHACL (CI job included): See `.github/workflows/validate-shacl.yml`
4. Review reference fixtures:
- Task fixtures: `examples/rdf/irreversible_task.ttl`, `examples/rdf/low_confidence_escalation.ttl`
- Culture fixture: `examples/rdf/culture_profile.ttl`

## License

Expand Down
32 changes: 32 additions & 0 deletions examples/culture_profile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
team_id: "team-alpha"
decision_rights_model: "RACI"
dissent_channels:
- name: "Minority Report"
preserves_dissent: true
anonymous: false
risk_policy:
tiers:
- name: "low"
requires_divergence: false
requires_minority_report: false
requires_second_order_effects: false
requires_audit_trail: true
escalation_required: false
- name: "high"
requires_divergence: true
requires_minority_report: true
requires_second_order_effects: true
requires_audit_trail: true
escalation_required: true
norms:
- name: "Disagree in writing before commit"
type: "decision"
incentives:
- name: "Escalate uncertainty early"
polarity: "reward"
rituals:
- name: "Incident Review"
cadence: "per-incident"
metrics:
- name: "Escalation Lead Time"
description: "Median time from uncertainty detection to human escalation."
25 changes: 25 additions & 0 deletions examples/rdf/culture_profile.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@prefix : <http://nkllon.org/hacp#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

:team-alpha-culture a :CultureProfile ;
:teamId "team-alpha" ;
:decisionRightsModel "RACI" ;
:hasDissentChannel :minority-report ;
:hasRiskPolicy :team-alpha-risk-policy .

:minority-report a :DissentChannel ;
:channelName "Minority Report" ;
:preservesDissent true .

:team-alpha-risk-policy a :RiskPolicy ;
:hasRiskTier :risk-tier-low, :risk-tier-high .

:risk-tier-low a :RiskTier ;
:tierName "low" ;
:requiresDivergence false ;
:requiresAuditTrail true .

:risk-tier-high a :RiskTier ;
:tierName "high" ;
:requiresDivergence true ;
:requiresAuditTrail true .
118 changes: 118 additions & 0 deletions schemas/culture-profile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# YAML schema (human-readable) for Culture Profile
title: HACP Culture Profile
type: object
required:
- team_id
- decision_rights_model
- dissent_channels
- risk_policy

properties:
team_id:
type: string
description: Unique team identifier (maps to Team entity in higher-level models).

decision_rights_model:
type: string
enum: [RACI, DACI, RAPID, Bespoke]
description: Team decision-rights topology.

dissent_channels:
type: array
minItems: 1
items:
type: object
required: [name, preserves_dissent]
properties:
name:
type: string
description: Human name for the dissent channel.
preserves_dissent:
type: boolean
description: If true, dissent must be durably recorded and linkable to a decision artifact.
anonymous:
type: boolean
default: false
description: If true, this channel supports anonymous dissent.

risk_policy:
type: object
required: [tiers]
properties:
tiers:
type: array
minItems: 2
items:
type: object
required: [name, requires_divergence, requires_audit_trail]
properties:
name:
type: string
description: "Tier name (for example: low/high or 1-4)."
requires_divergence:
type: boolean
description: If true, divergence-before-convergence is mandatory at this tier.
requires_minority_report:
type: boolean
default: false
requires_second_order_effects:
type: boolean
default: false
requires_audit_trail:
type: boolean
description: If true, decisions must preserve an auditable rationale chain.
escalation_required:
type: boolean
default: false
description: If true, explicit escalation to a defined authority group is required.

norms:
type: array
description: Explicit team norms (communication/decision/learning).
items:
type: object
required: [name, type]
properties:
name:
type: string
type:
type: string
enum: [communication, decision, review, learning, safety, execution]

incentives:
type: array
description: Explicit or implicit incentive signals that affect decision dynamics.
items:
type: object
required: [name, polarity]
properties:
name:
type: string
polarity:
type: string
enum: [reward, penalty, mixed]

rituals:
type: array
description: Team cadences (retro, design review, incident review, weekly planning).
items:
type: object
required: [name, cadence]
properties:
name:
type: string
cadence:
type: string
description: Example values include weekly, biweekly, monthly, or per-incident.

metrics:
type: array
description: Aggregate and non-personal signals used to operationalize culture.
items:
type: object
required: [name, description]
properties:
name:
type: string
description:
type: string
6 changes: 6 additions & 0 deletions schemas/task-manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ properties:
owner:
type: string
description: Human ID responsible for the task.il or org id)
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The description contains a typo: ".il or org id)" should likely be "(email or org id)" or similar. The text appears garbled.

Suggested change
description: Human ID responsible for the task.il or org id)
description: Human ID (email or org id) responsible for the task.

Copilot uses AI. Check for mistakes.
team_id:
type: string
description: Team identifier used to resolve culture and decision-rights context.
culture_profile_ref:
type: string
description: URI or file reference to the associated Culture Profile artifact.
operator:
type: string
description: Person initiating task
Expand Down
76 changes: 76 additions & 0 deletions shacl/culture-profile.shacl.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@prefix : <http://nkllon.org/hacp#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

:CultureProfileShape
a sh:NodeShape ;
sh:targetClass :CultureProfile ;
sh:property [
sh:path :teamId ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:message "CultureProfile MUST declare teamId." ;
] ;
sh:property [
sh:path :decisionRightsModel ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:message "CultureProfile MUST declare decisionRightsModel." ;
] ;
sh:property [
sh:path :hasDissentChannel ;
sh:minCount 1 ;
sh:message "CultureProfile MUST declare at least one dissent channel." ;
] ;
sh:property [
sh:path :hasRiskPolicy ;
sh:minCount 1 ;
sh:message "CultureProfile MUST declare a risk policy." ;
] .

:RiskPolicyShape
a sh:NodeShape ;
sh:targetClass :RiskPolicy ;
sh:property [
sh:path :hasRiskTier ;
sh:minCount 2 ;
sh:message "RiskPolicy MUST define at least two risk tiers." ;
] .

:RiskTierShape
a sh:NodeShape ;
sh:targetClass :RiskTier ;
sh:property [
sh:path :tierName ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:message "RiskTier MUST have tierName." ;
] ;
sh:property [
sh:path :requiresDivergence ;
sh:datatype xsd:boolean ;
sh:minCount 1 ;
sh:message "RiskTier MUST specify requiresDivergence (true/false)." ;
] ;
sh:property [
sh:path :requiresAuditTrail ;
sh:datatype xsd:boolean ;
sh:minCount 1 ;
sh:message "RiskTier MUST specify requiresAuditTrail (true/false)." ;
] .

:DissentChannelShape
a sh:NodeShape ;
sh:targetClass :DissentChannel ;
sh:property [
sh:path :channelName ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:message "DissentChannel MUST have channelName." ;
] ;
sh:property [
sh:path :preservesDissent ;
sh:datatype xsd:boolean ;
sh:minCount 1 ;
sh:message "DissentChannel MUST specify preservesDissent (true/false)." ;
] .
17 changes: 8 additions & 9 deletions shacl/hacp-core.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,14 @@
:AuthorityBoundaryShape a sh:NodeShape ;
sh:targetClass :Execution ;
sh:message "Execution of irreversible actions requires explicit human approval." ;
sh:property [
sh:path :reversibility ;
sh:hasValue "Irreversible"^^xsd:string ;
sh:severity sh:Violation ;
] ;
sh:property [
sh:path :humanApproval ;
sh:minCount 1 ;
sh:severity sh:Violation ;
sh:sparql [
sh:message "Execution with reversibility=Irreversible MUST include humanApproval." ;
sh:select """
SELECT $this WHERE {
$this :reversibility ?rev .
FILTER(str(?rev) = "Irreversible" && NOT EXISTS { $this :humanApproval ?approval })
}
""" ;
] .

:EscalationOnUncertaintyShape a sh:NodeShape ;
Expand Down