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
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
fail-fast: false
matrix:
include:
- language: javascript-typescript
- language: actions
build-mode: none
steps:
- name: Checkout
Expand Down
6 changes: 1 addition & 5 deletions .github/workflows/instant-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
dispatch:
runs-on: ubuntu-latest
timeout-minutes: 15
if: ${{ secrets.FARM_DISPATCH_TOKEN != '' }}
steps:
- name: Trigger Propagation
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v3
Expand All @@ -32,8 +33,3 @@ jobs:

- name: Confirm
run: echo "::notice::Propagation triggered for ${{ github.event.repository.name }}"

- name: K9-SVC Validation
run: |
echo "K9-SVC validation"
[ -d .machine_readable/contractiles ] && echo "Contractiles present" || echo "No contractiles"
14 changes: 7 additions & 7 deletions .github/workflows/mirror.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
mirror-gitlab:
runs-on: ubuntu-latest
timeout-minutes: 15
if: vars.GITLAB_MIRROR_ENABLED == 'true'
if: vars.GITLAB_MIRROR_ENABLED == 'true' && secrets.GITLAB_SSH_KEY != ''
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -26,7 +26,7 @@ jobs:
mirror-bitbucket:
runs-on: ubuntu-latest
timeout-minutes: 15
if: vars.BITBUCKET_MIRROR_ENABLED == 'true'
if: vars.BITBUCKET_MIRROR_ENABLED == 'true' && secrets.BITBUCKET_SSH_KEY != ''
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -42,7 +42,7 @@ jobs:
mirror-codeberg:
runs-on: ubuntu-latest
timeout-minutes: 15
if: vars.CODEBERG_MIRROR_ENABLED == 'true'
if: vars.CODEBERG_MIRROR_ENABLED == 'true' && secrets.CODEBERG_SSH_KEY != ''
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -58,7 +58,7 @@ jobs:
mirror-sourcehut:
runs-on: ubuntu-latest
timeout-minutes: 15
if: vars.SOURCEHUT_MIRROR_ENABLED == 'true'
if: vars.SOURCEHUT_MIRROR_ENABLED == 'true' && secrets.SOURCEHUT_SSH_KEY != ''
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -74,7 +74,7 @@ jobs:
mirror-disroot:
runs-on: ubuntu-latest
timeout-minutes: 15
if: vars.DISROOT_MIRROR_ENABLED == 'true'
if: vars.DISROOT_MIRROR_ENABLED == 'true' && secrets.DISROOT_SSH_KEY != ''
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -90,7 +90,7 @@ jobs:
mirror-gitea:
runs-on: ubuntu-latest
timeout-minutes: 15
if: vars.GITEA_MIRROR_ENABLED == 'true'
if: vars.GITEA_MIRROR_ENABLED == 'true' && secrets.GITEA_SSH_KEY != ''
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand All @@ -106,7 +106,7 @@ jobs:
mirror-radicle:
runs-on: ubuntu-latest
timeout-minutes: 15
if: vars.RADICLE_MIRROR_ENABLED == 'true'
if: vars.RADICLE_MIRROR_ENABLED == 'true' && secrets.RADICLE_KEY != ''
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
Expand Down
3 changes: 3 additions & 0 deletions .machine_readable/bot_directives/coverage.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# Reference: ADR-002 in standards/agentic-a2ml/docs/

[metadata]
name = "Axiology.jl-coverage"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
last-updated = "2026-03-24"

Expand Down
3 changes: 3 additions & 0 deletions .machine_readable/bot_directives/debt.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# Reference: ADR-002 in standards/agentic-a2ml/docs/

[metadata]
name = "Axiology.jl-debt"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
last-updated = "2026-03-24"

Expand Down
3 changes: 3 additions & 0 deletions .machine_readable/bot_directives/git-private-farm.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
# workflow .github/workflows/instant-sync.yml (secret NAME only; no values).

[metadata]
name = "Axiology.jl-git-private-farm"
project = "Axiology.jl"
schema_version = "1.0"
repo = "Axiology.jl"
last-updated = "2026-06-13"
owner = "hyperpolymath"
Expand Down
3 changes: 3 additions & 0 deletions .machine_readable/bot_directives/gitbot-fleet.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
# per-bot directive files (.machine_readable/bot_directives/*.a2ml).

[metadata]
name = "Axiology.jl-gitbot-fleet"
project = "Axiology.jl"
schema_version = "1.0"
repo = "Axiology.jl"
last-updated = "2026-06-13"
owner = "hyperpolymath"
Expand Down
3 changes: 3 additions & 0 deletions .machine_readable/bot_directives/hypatia.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
# bot_directives standard (hypatia / gitbot-fleet / git-private-farm trio).

[metadata]
name = "Axiology.jl-hypatia"
project = "Axiology.jl"
schema_version = "1.0"
repo = "Axiology.jl"
last-updated = "2026-06-13"
owner = "hyperpolymath"
Expand Down
3 changes: 3 additions & 0 deletions .machine_readable/bot_directives/methodology.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# Reference: ADR-002 in standards/agentic-a2ml/docs/

[metadata]
name = "Axiology.jl-methodology"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
last-updated = "2026-03-24"
spec = "https://github.com/hyperpolymath/standards/blob/main/agentic-a2ml/docs/ADR-002-methodology-layer.adoc"
Expand Down
7 changes: 7 additions & 0 deletions .machine_readable/contractiles/Adjustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
# the rsr-template-repo Adjustfile (drift checks about "template placeholders"
# and "template version drift").

[metadata]
name = "Axiology.jl-adjustfile"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
last-updated = "2026-06-13"

@abstract:
Drift tolerances and corrective actions for Axiology.jl. Unlike MUST (hard
gate), ADJUST tracks cumulative drift against tolerance bands and proposes
Expand Down
3 changes: 3 additions & 0 deletions .machine_readable/contractiles/Bustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
# by this flat form). Added 2026-06-13.

[bustfile]
name = "Axiology.jl-bustfile"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
format = "a2ml"
repo = "Axiology.jl"
Expand Down
3 changes: 3 additions & 0 deletions .machine_readable/contractiles/Dustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# is superseded by this flat form). Added 2026-06-13.

[dustfile]
name = "Axiology.jl-dustfile"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
format = "a2ml"
repo = "Axiology.jl"
Expand Down
7 changes: 7 additions & 0 deletions .machine_readable/contractiles/Intentfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
# carried was the un-substituted rsr-template-repo Intentfile (its intents
# were "repo-initialization / contractile-completeness for the template").

[metadata]
name = "Axiology.jl-intentfile"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
last-updated = "2026-06-13"

@abstract:
North-star for Axiology.jl: making ethical and economic values explicit,
verifiable, and composable for machine learning systems. The invariant goal
Expand Down
7 changes: 7 additions & 0 deletions .machine_readable/contractiles/Mustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
# delivered layout is FLAT. The checks below match Axiology.jl's ACTUAL
# flat layout. The template drift is recorded for upstream fix.

[metadata]
name = "Axiology.jl-mustfile"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
last-updated = "2026-06-13"

@abstract:
Physical-state invariants for Axiology.jl. Hard requirements — CI and
pre-commit hooks fail if any critical check fails.
Expand Down
7 changes: 7 additions & 0 deletions .machine_readable/contractiles/Trustfile.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
# "template files"; the integrity invariants below are kept and apply to
# Axiology.jl.

[metadata]
name = "Axiology.jl-trustfile"
project = "Axiology.jl"
schema_version = "1.0"
version = "1.0.0"
last-updated = "2026-06-13"

@abstract:
Trust boundaries and integrity checks for Axiology.jl. Defines what AI agents
may do autonomously and what requires human approval, plus checks that verify
Expand Down
2 changes: 1 addition & 1 deletion .machine_readable/self-validating/template-kennel.k9.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ K9!
allow_subprocess = false,
},
metadata = {
name = "TODO: component-name",
name = "template-kennel",
version = "1.0.0",
description = "TODO: Brief description of what this component contains",
author = "Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>",
Expand Down
4 changes: 2 additions & 2 deletions .machine_readable/self-validating/template-yard.k9.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ K9!
allow_subprocess = false,
},
metadata = {
name = "TODO: component-name",
name = "template-yard",
version = "1.0.0",
description = "TODO: Brief description with validation details",
description = "Yard-level template: validated configuration with Nickel contracts",
author = "Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>",
},
},
Expand Down
5 changes: 5 additions & 0 deletions src/Axiology.jl
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ using LinearAlgebra
# Core value types
export Value, Fairness, Welfare, Profit, Efficiency, Safety
export FairnessMetric, WelfareMetric, EfficiencyMetric
# Enum variant exports
export demographic_parity_metric, equalized_odds_metric, equal_opportunity_metric
export disparate_impact_metric, individual_fairness_metric
export utilitarian_metric, rawlsian_metric, egalitarian_metric
export pareto_metric, kaldor_hicks_metric, computation_time_metric
export satisfy, maximize, verify_value
export pareto_frontier, dominated, value_score
export weighted_score, normalize_scores
Expand Down
33 changes: 22 additions & 11 deletions src/fairness.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ function equalized_odds(predictions::AbstractVector{<:Real}, labels::AbstractVec
if length(unique_groups) < 2
return 0.0
end
tpr_disparities = Float64[]
fpr_disparities = Float64[]
# Only include groups that have positives (for TPR) or negatives (for FPR).
# Groups with no positives have undefined TPR; including them as 0.0 creates
# artificial disparity when other groups achieve TPR=1.0 on their (all-positive) data.
tprs = Float64[]
fprs = Float64[]
for group in unique_groups
group_mask = protected_attributes .== group
group_preds = predictions[group_mask]
Expand All @@ -34,13 +37,17 @@ function equalized_odds(predictions::AbstractVector{<:Real}, labels::AbstractVec
fp = sum((group_preds .== 1) .& (group_labels .== 0))
tn = sum((group_preds .== 0) .& (group_labels .== 0))
fn = sum((group_preds .== 0) .& (group_labels .== 1))
tpr = (tp + fn) > 0 ? tp / (tp + fn) : 0.0
fpr = (fp + tn) > 0 ? fp / (fp + tn) : 0.0
push!(tpr_disparities, tpr)
push!(fpr_disparities, fpr)
# Only count TPR for groups that have at least one positive example
if (tp + fn) > 0
push!(tprs, tp / (tp + fn))
end
# Only count FPR for groups that have at least one negative example
if (fp + tn) > 0
push!(fprs, fp / (fp + tn))
end
end
max_tpr_disparity = maximum(tpr_disparities) - minimum(tpr_disparities)
max_fpr_disparity = maximum(fpr_disparities) - minimum(fpr_disparities)
max_tpr_disparity = length(tprs) >= 2 ? maximum(tprs) - minimum(tprs) : 0.0
max_fpr_disparity = length(fprs) >= 2 ? maximum(fprs) - minimum(fprs) : 0.0
return max(max_tpr_disparity, max_fpr_disparity)
end

Expand All @@ -51,17 +58,21 @@ function equal_opportunity(predictions::AbstractVector{<:Real}, labels::Abstract
if length(unique_groups) < 2
return 0.0
end
# Only include groups that have at least one positive label.
# Groups with no positives have undefined TPR; including them as 0.0 creates
# artificial disparity when other groups achieve TPR=1.0 on their (all-positive) data.
tprs = Float64[]
for group in unique_groups
group_mask = protected_attributes .== group
group_preds = predictions[group_mask]
group_labels = labels[group_mask]
tp = sum((group_preds .== 1) .& (group_labels .== 1))
fn = sum((group_preds .== 0) .& (group_labels .== 1))
tpr = (tp + fn) > 0 ? tp / (tp + fn) : 0.0
push!(tprs, tpr)
if (tp + fn) > 0
push!(tprs, tp / (tp + fn))
end
end
return maximum(tprs) - minimum(tprs)
return length(tprs) >= 2 ? maximum(tprs) - minimum(tprs) : 0.0
end

function disparate_impact(predictions::AbstractVector, protected_attributes::AbstractVector)::Float64
Expand Down
14 changes: 11 additions & 3 deletions src/optimization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,17 @@ function value_score(value::Value, state::Dict)::Float64
end
end

# Normalize welfare value - simple approach: return welfare_val directly
# More sophisticated normalization could be added based on expected ranges
return welfare_val
# Normalize welfare value to [0,1] using max_welfare from state if provided,
# otherwise clamp the raw value to [0,1].
max_welfare = get(state, :max_welfare, nothing)
if !isnothing(max_welfare) && max_welfare > 0.0
return min(1.0, max(0.0, welfare_val / max_welfare))
else
# For egalitarian (negative values mean inequality), map to [0,1]:
# 0.0 = perfect equality, negative = inequality → score = max(0, 1 + welfare_val)
# For utilitarian/rawlsian without max_welfare, clamp to [0,1].
return min(1.0, max(0.0, welfare_val))
end

elseif value isa Profit
profit = get(state, :profit, nothing)
Expand Down
5 changes: 3 additions & 2 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ struct Fairness <: Value
protected_attributes::Vector{Symbol} = Symbol[],
threshold::Float64 = 0.05,
weight::Float64 = 1.0)
@assert metric in [:demographic_parity, :equalized_odds, :equal_opportunity,
:disparate_impact, :individual_fairness] "Invalid fairness metric: `$(metric)`. Must be one of $(instances(FairnessMetric))."
metric in [:demographic_parity, :equalized_odds, :equal_opportunity,
:disparate_impact, :individual_fairness] ||
error("Invalid fairness metric: `$(metric)`. Must be one of $(instances(FairnessMetric)).")
@assert threshold >= 0.0 && threshold <= 1.0 "Threshold must be in [0,1]."
@assert weight >= 0.0 "Weight must be non-negative."
new(metric, protected_attributes, threshold, weight)
Expand Down
5 changes: 4 additions & 1 deletion src/welfare.jl
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ function verify_value(value::Safety, proof::Dict)::Bool
isnothing(verified) && error("proof must contain :verified field")
isa(verified, Bool) || error("proof[:verified] must be a Bool, got $(typeof(verified))")

# For critical safety values, require a prover
# If not verified, return false immediately — no need to check prover.
!verified && return false

# For critical safety values that are verified, require a prover
if value.critical
prover = get(proof, :prover, nothing)
isnothing(prover) && error("Critical safety proofs must contain :prover field")
Expand Down
Loading