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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
116 changes: 116 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# SPDX-FileCopyrightText: 2026 PullsarDev
# SPDX-License-Identifier: Apache-2.0

name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
build-and-test:
name: ${{ matrix.name }} Debug (${{ matrix.checks }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
compiler: [msvc, clang, gcc]
checks: [default, constrained]
include:
- compiler: msvc
name: Windows MSVC
os: windows-latest
cmake_args: ""
- compiler: clang
name: Linux Clang
os: ubuntu-latest
cmake_args: -DCMAKE_CXX_COMPILER=clang++
- compiler: gcc
name: Linux GCC
os: ubuntu-latest
cmake_args: -DCMAKE_CXX_COMPILER=g++
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Configure
shell: bash
run: >
cmake -S . -B build
-DCMAKE_BUILD_TYPE=Debug
-DINTEREST_WARNINGS_AS_ERRORS=ON
-DINTEREST_CONSTRAINED_BUILD=${{ matrix.checks == 'constrained' && 'ON' || 'OFF' }}
${{ matrix.cmake_args }}
- name: Build
run: cmake --build build --config Debug --parallel 2
- name: Test
run: ctest --test-dir build -C Debug --output-on-failure --no-tests=error

optimized:
name: ${{ matrix.name }} Release
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- name: Windows MSVC
os: windows-latest
cmake_args: ""
- name: Linux Clang
os: ubuntu-latest
cmake_args: -DCMAKE_CXX_COMPILER=clang++
- name: Linux GCC
os: ubuntu-latest
cmake_args: -DCMAKE_CXX_COMPILER=g++
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Configure
run: >
cmake -S . -B build-release
-DCMAKE_BUILD_TYPE=Release
-DINTEREST_WARNINGS_AS_ERRORS=ON
${{ matrix.cmake_args }}
- name: Build
run: cmake --build build-release --config Release --parallel 2
- name: Test
run: ctest --test-dir build-release -C Release --output-on-failure --no-tests=error
- name: Build from a parent CMake project
run: >
cmake -S examples/consumer -B build-consumer
-DCMAKE_BUILD_TYPE=Release
${{ matrix.cmake_args }}
- name: Build consumer
run: cmake --build build-consumer --config Release --parallel 2
- name: Test consumer
run: ctest --test-dir build-consumer -C Release --output-on-failure --no-tests=error

sanitizers:
name: Linux Clang ASan + UBSan
runs-on: ubuntu-latest
timeout-minutes: 15
env:
ASAN_OPTIONS: detect_leaks=1
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Configure
run: >
cmake -S . -B build-sanitizers
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_BUILD_TYPE=Debug
-DINTEREST_WARNINGS_AS_ERRORS=ON
-DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer"
- name: Build
run: cmake --build build-sanitizers --parallel 2
- name: Test
run: ctest --test-dir build-sanitizers --output-on-failure --no-tests=error
99 changes: 99 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# SPDX-FileCopyrightText: 2026 PullsarDev
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20)

project(InterestEngine VERSION 0.1.0 LANGUAGES CXX)

set(CMAKE_CXX_EXTENSIONS OFF)

set(interest_top_level OFF)
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
set(interest_top_level ON)
endif()

option(INTEREST_BUILD_TESTS "Build the Interest Engine tests" ${interest_top_level})
option(INTEREST_BUILD_BENCHMARKS "Build the Step benchmark harness" ${interest_top_level})
option(INTEREST_BUILD_EXAMPLES "Build the basic API example" ${interest_top_level})
option(INTEREST_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF)
option(INTEREST_CONSTRAINED_BUILD "Compile without exceptions or RTTI, with shadow warnings" OFF)

if(INTEREST_CONSTRAINED_BUILD AND MSVC)
# CMake's default MSVC flags enable exceptions; remove them so /EHs-c- is not overridden.
string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif()

# Project-wide compile settings shared by the kernel and its tests.
add_library(interest_build_settings INTERFACE)
target_compile_features(interest_build_settings INTERFACE cxx_std_20)

if(MSVC)
target_compile_options(interest_build_settings INTERFACE
/W4 /permissive- /utf-8 /Zc:__cplusplus /Zc:preprocessor)
if(INTEREST_WARNINGS_AS_ERRORS)
target_compile_options(interest_build_settings INTERFACE /WX)
endif()
if(INTEREST_CONSTRAINED_BUILD)
# C4456-C4459: declaration hides previous local/parameter/member/global.
target_compile_options(interest_build_settings INTERFACE
/EHs-c- /GR- /we4456 /we4457 /we4458 /we4459)
target_compile_definitions(interest_build_settings INTERFACE _HAS_EXCEPTIONS=0)
endif()
else()
target_compile_options(interest_build_settings INTERFACE
-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wdouble-promotion)
if(INTEREST_WARNINGS_AS_ERRORS)
target_compile_options(interest_build_settings INTERFACE -Werror)
endif()
if(INTEREST_CONSTRAINED_BUILD)
target_compile_options(interest_build_settings INTERFACE
-fno-exceptions -fno-rtti -Wshadow)
endif()
endif()

add_library(interest_core STATIC
src/step.cpp
src/version.cpp)
add_library(InterestEngine::Core ALIAS interest_core)
target_include_directories(interest_core PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(interest_core PRIVATE interest_build_settings)
target_compile_features(interest_core PUBLIC cxx_std_20)

if(INTEREST_BUILD_TESTS)
enable_testing()

add_executable(interest_smoke_test tests/smoke_test.cpp)
target_link_libraries(interest_smoke_test PRIVATE interest_core interest_build_settings)
if(INTEREST_CONSTRAINED_BUILD)
# Lets the smoke test confirm the constraints were actually applied.
target_compile_definitions(interest_smoke_test PRIVATE INTEREST_EXPECT_CONSTRAINED_BUILD=1)
endif()

add_test(NAME interest_smoke_test COMMAND interest_smoke_test)

foreach(suite IN ITEMS scenario scoring allocation c07c temporal baseline rate observers novelty retention c18 property)
add_executable(interest_${suite}_test tests/step_${suite}_test.cpp)
target_link_libraries(interest_${suite}_test PRIVATE interest_core interest_build_settings)
add_test(NAME interest_${suite}_test COMMAND interest_${suite}_test)
endforeach()

endif()

if(INTEREST_BUILD_BENCHMARKS)
# CTest checks harness invariants; use optimized manual runs for timing evidence.
add_executable(interest_step_benchmark benchmarks/step_benchmark.cpp)
target_include_directories(interest_step_benchmark PRIVATE tests)
target_link_libraries(interest_step_benchmark PRIVATE interest_core interest_build_settings)
if(INTEREST_BUILD_TESTS)
add_test(NAME interest_benchmark_smoke COMMAND interest_step_benchmark --smoke)
endif()
endif()

if(INTEREST_BUILD_EXAMPLES)
add_executable(interest_basic_example examples/basic.cpp)
target_link_libraries(interest_basic_example PRIVATE InterestEngine::Core interest_build_settings)
if(INTEREST_BUILD_TESTS)
add_test(NAME interest_basic_example COMMAND interest_basic_example)
endif()
endif()
54 changes: 54 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Contributing

Use a focused pull request that describes the problem, the resulting behavior and
the checks actually run. Include a minimal reproducible scenario for a bug report:
snapshots, profile and context values, explicit times, previous state, status and
decision/trace. Remove private game data before submitting.

The production library uses C++20 and the standard library only. Preserve explicit
time, caller ownership, allocation-free prepared steps, validation precedence and
deterministic candidate ordering. Changes to numerical behavior or public contracts
need deliberate review and regression evidence. Keep host integration outside the
kernel.

Build with warnings treated as errors and run CTest in Debug and Release. Follow
the README commands; also check `INTEREST_CONSTRAINED_BUILD=ON` where practical.
The CI matrix adds GCC, Clang, MSVC and Clang address/undefined-behavior sanitizers.
Report unavailable configurations honestly.

Tests use dependency-free checks that remain active under `NDEBUG`. The suites cover:

| Area | Files in `tests/` |
|---|---|
| Version and compiler constraints | `smoke_test.cpp` |
| Snapshots, rejection paths, gates and formulas | `step_scenario_test.cpp`, `step_scoring_test.cpp` |
| Allocation and capacity contracts | `step_allocation_test.cpp` |
| Alternative formula controls | `step_c07c_test.cpp` |
| Hysteresis, dwell, switching and time extremes | `step_temporal_test.cpp` |
| Stateless baseline, update rates, observer variation | `step_baseline_test.cpp`, `step_rate_test.cpp`, `step_observers_test.cpp` |
| Novelty, saturation and sensitivity | `step_novelty_test.cpp`, `step_retention_test.cpp` |
| Sustained churn, exact replay, permutations and failed-step atomicity | `step_c18_test.cpp` |
| Geometry transforms, finite extremes and time translation/decay | `step_property_test.cpp` |

In test output, **B1** is stateless selection of the leading candidate at or above
the acquisition threshold. **B2** uses the same contextual scores with the kernel's
temporal policy; novelty is disabled for these baseline comparisons. B1 is read
from the Step trace so the comparison uses identical scores. Ablations change one
temporal mechanism through profile parameters.

Labels such as `C07c`, `C12` and `C18` are stable scenario identifiers: preference
formula comparison, update-rate comparison and sustained churn, respectively.
Letter suffixes distinguish variants. The test functions and nearby comments
describe each scenario; these labels are not public API concepts.

The benchmark also checks paired inputs, replay, trace independence and allocation
counts in smoke mode. See [benchmark methodology](docs/benchmarks.md) before making
performance claims. Do not use CI smoke timings as performance results.

When editing the README example, update `examples/basic.cpp` identically and run
`interest_basic_example`. The parent-project fixture in `examples/consumer` checks
the exported CMake target and that utilities default off when embedded.

By submitting a contribution, you agree to license it under Apache-2.0, unless
explicitly stated otherwise. Preserve third-party attribution and identify the
origin and license of any proposed external material.
Loading
Loading