feat(kernel): division-of-labor society — per-specialist writable field workspaces #282
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Build and test automation | |
| name: CI/CD Pipeline | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| pull_request: | |
| jobs: | |
| build: | |
| name: Build and Test | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| arch: [x86_64] | |
| build_type: [debug, release] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| # Install cross-compiler toolchain (Ubuntu 24.04 compatible) | |
| sudo apt-get install -y build-essential bison flex libgmp3-dev libmpc-dev libmpfr-dev texinfo | |
| sudo apt-get install -y gdb-multiarch qemu-system-x86 grub-pc-bin xorriso nasm | |
| # Use system gcc with -m64 flag for x86_64 target, or install cross-compiler from source | |
| # For now, use the standard gcc with appropriate flags | |
| which gcc && gcc --version | |
| - name: Build kernel (Debug) | |
| if: matrix.build_type == 'debug' | |
| run: | | |
| make clean | |
| make BUILD_TYPE=debug | |
| - name: Build kernel (Release) | |
| if: matrix.build_type == 'release' | |
| run: | | |
| make clean | |
| make BUILD_TYPE=release | |
| - name: Run unit tests | |
| run: | | |
| make test | |
| - name: Check kernel boots | |
| timeout-minutes: 5 | |
| run: | | |
| timeout 30s make run || echo "Boot test completed (timeout expected)" | |
| - name: Generate build artifacts | |
| run: | | |
| make dump > build_dump.txt | |
| ls -la build/ > build_files.txt | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: quantumos-build-${{ matrix.arch }}-${{ matrix.build_type }} | |
| path: | | |
| build/ | |
| build_dump.txt | |
| build_files.txt | |
| retention-days: 30 | |
| security: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check for unsafe functions | |
| run: | | |
| echo "Scanning for unsafe C functions..." | |
| UNSAFE_FOUND=0 | |
| # Check for dangerous functions that should never be used. | |
| # Match actual call sites (word boundary + open paren), not any | |
| # substring — "num_targets" and comment prose like "the caller | |
| # gets nothing" are not gets() calls. | |
| if find . -name "*.c" -o -name "*.h" | xargs grep -lE "\b(gets|sprintf)[[:space:]]*\(" 2>/dev/null | grep -v test; then | |
| echo "ERROR: Found usage of dangerous functions (gets, sprintf without bounds)" | |
| UNSAFE_FOUND=1 | |
| fi | |
| # Warn about potentially unsafe functions (strcpy, strcat) | |
| echo "" | |
| echo "Checking for potentially unsafe functions (warnings)..." | |
| find . -name "*.c" -o -name "*.h" | xargs grep -n "strcpy\|strcat" 2>/dev/null || true | |
| # Check for shell execution functions (should be rare in kernel code) | |
| if find . -name "*.c" | xargs grep -lE "\bsystem[[:space:]]*\(" 2>/dev/null | grep -v test; then | |
| echo "ERROR: Found system() calls in non-test code" | |
| UNSAFE_FOUND=1 | |
| fi | |
| if [ $UNSAFE_FOUND -eq 1 ]; then | |
| echo "Security scan FAILED" | |
| exit 1 | |
| fi | |
| echo "Security scan PASSED" | |
| code-quality: | |
| name: Code Quality | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install tools | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y clang-format cppcheck | |
| - name: Check code formatting | |
| run: | | |
| echo "Checking code formatting with clang-format..." | |
| FORMAT_ERRORS=0 | |
| for file in $(find . -name "*.c" -o -name "*.h" | grep -v build/); do | |
| if ! clang-format --dry-run --Werror "$file" 2>/dev/null; then | |
| echo "Formatting issue in: $file" | |
| FORMAT_ERRORS=$((FORMAT_ERRORS + 1)) | |
| fi | |
| done | |
| if [ $FORMAT_ERRORS -gt 0 ]; then | |
| echo "Found $FORMAT_ERRORS files with formatting issues" | |
| echo "Run 'clang-format -i <file>' to fix" | |
| exit 1 | |
| fi | |
| echo "Code formatting check PASSED" | |
| - name: Static analysis | |
| run: | | |
| echo "Running cppcheck static analysis..." | |
| # Run cppcheck but filter out noise from system headers and focus on errors | |
| find . -name "*.c" -not -path "./build/*" | xargs cppcheck \ | |
| --enable=warning,performance,portability \ | |
| --error-exitcode=1 \ | |
| --suppress=missingIncludeSystem \ | |
| --suppress=unusedFunction \ | |
| -I kernel/include \ | |
| 2>&1 | |
| echo "Static analysis PASSED" | |
| - name: Check for TODO/FIXME comments | |
| run: | | |
| echo "Checking for TODO/FIXME/XXX comments..." | |
| grep -rn "TODO\|FIXME\|XXX" --include="*.c" --include="*.h" . || echo "No TODO/FIXME/XXX comments found" | |
| echo "(This is informational only, not a failure)" | |
| documentation: | |
| name: Documentation Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Need full history to compare changes | |
| - name: Check required documentation files exist | |
| run: | | |
| echo "Checking required documentation files..." | |
| MISSING=0 | |
| if [ ! -f README.md ]; then | |
| echo "ERROR: README.md is missing" | |
| MISSING=1 | |
| fi | |
| if [ ! -f CONTRIBUTING.md ]; then | |
| echo "ERROR: CONTRIBUTING.md is missing" | |
| MISSING=1 | |
| fi | |
| if [ ! -f LICENSE ]; then | |
| echo "ERROR: LICENSE is missing" | |
| MISSING=1 | |
| fi | |
| if [ $MISSING -eq 1 ]; then | |
| exit 1 | |
| fi | |
| echo "Required documentation files exist" | |
| documentation-sync: | |
| name: Documentation Sync Check | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Need full history to compare changes | |
| - name: Check documentation updated with code changes | |
| run: | | |
| echo "Checking if documentation is updated with code changes..." | |
| # Get the base branch (usually main or develop) | |
| BASE_BRANCH="${{ github.base_ref }}" | |
| # Get list of changed files in this PR | |
| CHANGED_FILES=$(git diff --name-only origin/$BASE_BRANCH...HEAD) | |
| echo "Changed files in this PR:" | |
| echo "$CHANGED_FILES" | |
| echo "" | |
| # Check if any C source or header files were changed | |
| CODE_CHANGED=0 | |
| if echo "$CHANGED_FILES" | grep -E '\.(c|h)$' | grep -v test; then | |
| CODE_CHANGED=1 | |
| echo "Code files (.c/.h) were modified" | |
| fi | |
| # Check if any documentation files were changed | |
| DOCS_CHANGED=0 | |
| if echo "$CHANGED_FILES" | grep -E '\.(md|rst|txt)$|^docs/'; then | |
| DOCS_CHANGED=1 | |
| echo "Documentation files were modified" | |
| fi | |
| # If code changed but no docs changed, check if it's a significant change | |
| if [ $CODE_CHANGED -eq 1 ] && [ $DOCS_CHANGED -eq 0 ]; then | |
| echo "" | |
| echo "WARNING: Code files were changed but no documentation was updated." | |
| echo "" | |
| # Check for new public functions in headers (these require documentation) | |
| NEW_FUNCS=$(git diff origin/$BASE_BRANCH...HEAD -- '*.h' | grep "^+" | grep -E "^[^/]*\(" | grep -v "static" | grep -v "#define" || true) | |
| if [ -n "$NEW_FUNCS" ]; then | |
| echo "ERROR: New public functions were added without documentation updates:" | |
| echo "$NEW_FUNCS" | |
| echo "" | |
| echo "Please update the relevant documentation (README.md, API docs, etc.)" | |
| echo "to describe these new functions." | |
| exit 1 | |
| fi | |
| # Check for changes to public API (function signature changes) | |
| API_CHANGES=$(git diff origin/$BASE_BRANCH...HEAD -- 'kernel/include/*.h' | grep "^[-+]" | grep -E "(status_t|void|int|uint|bool).*\(" || true) | |
| if [ -n "$API_CHANGES" ]; then | |
| echo "WARNING: API changes detected in headers:" | |
| echo "$API_CHANGES" | |
| echo "" | |
| echo "Consider updating documentation to reflect these API changes." | |
| echo "(This is a warning, not blocking the PR)" | |
| fi | |
| fi | |
| echo "" | |
| echo "Documentation sync check completed" | |
| - name: Validate API documentation matches headers | |
| run: | | |
| echo "Validating API documentation consistency..." | |
| # Run the API consistency check script if it exists | |
| if [ -f ./scripts/check-api-consistency.sh ]; then | |
| chmod +x ./scripts/check-api-consistency.sh | |
| ./scripts/check-api-consistency.sh | |
| else | |
| echo "API consistency script not found, skipping" | |
| fi | |
| performance: | |
| name: Performance Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 time nasm | |
| - name: Build kernel | |
| run: make clean && make | |
| - name: Performance benchmarks | |
| run: | | |
| echo "=== Performance Benchmarks ===" > performance_report.txt | |
| echo "" >> performance_report.txt | |
| # Measure clean build time | |
| echo "Measuring clean build time..." | |
| make clean | |
| START_TIME=$(date +%s.%N) | |
| make | |
| END_TIME=$(date +%s.%N) | |
| BUILD_TIME=$(echo "$END_TIME - $START_TIME" | bc) | |
| echo "Clean build time: ${BUILD_TIME}s" >> performance_report.txt | |
| # Measure incremental build time (no changes) | |
| echo "Measuring incremental build time..." | |
| START_TIME=$(date +%s.%N) | |
| make | |
| END_TIME=$(date +%s.%N) | |
| INCR_TIME=$(echo "$END_TIME - $START_TIME" | bc) | |
| echo "Incremental build time: ${INCR_TIME}s" >> performance_report.txt | |
| # Measure boot time | |
| echo "Measuring boot time..." | |
| timeout 10s qemu-system-x86_64 -kernel build/x86_64/kernel.elf32 \ | |
| -serial stdio -m 128M -display none -no-reboot 2>&1 | head -50 > boot_output.txt || true | |
| echo "" >> performance_report.txt | |
| echo "Boot output captured (first 50 lines)" >> performance_report.txt | |
| # Check kernel binary size | |
| KERNEL_SIZE=$(stat -c%s build/x86_64/kernel.elf 2>/dev/null || echo "unknown") | |
| echo "Kernel size: ${KERNEL_SIZE} bytes" >> performance_report.txt | |
| echo "" >> performance_report.txt | |
| echo "Performance tests completed at $(date)" >> performance_report.txt | |
| cat performance_report.txt | |
| - name: Upload performance data | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: performance-data | |
| path: | | |
| performance_report.txt | |
| boot_output.txt | |
| retention-days: 7 | |
| code-coverage: | |
| name: Code Coverage | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm lcov | |
| # gcov (--coverage) cannot instrument the freestanding kernel image: | |
| # the gcov runtime needs libc, the kernel links with -nostdlib, so an | |
| # instrumented build always dies at link on undefined __gcov_* symbols. | |
| # Build normally, exercise the test paths, and let the report below say | |
| # honestly that line coverage is not measurable until an in-kernel gcov | |
| # runtime exists. Functional coverage lives in the smoke/integration jobs. | |
| - name: Build kernel | |
| run: | | |
| make clean | |
| make | |
| - name: Run tests | |
| run: | | |
| # Run the test suite | |
| make test || true | |
| # Boot the kernel briefly to execute initialization code paths | |
| timeout 5s qemu-system-x86_64 -kernel build/x86_64/kernel.elf32 \ | |
| -serial stdio -m 128M -display none -no-reboot 2>&1 || true | |
| - name: Generate coverage report | |
| run: | | |
| echo "=== Code Coverage Report ===" > coverage_summary.txt | |
| # Collect coverage data | |
| lcov --capture --directory . --output-file coverage.info 2>/dev/null || true | |
| # Filter out system headers | |
| lcov --remove coverage.info '/usr/*' --output-file coverage.info 2>/dev/null || true | |
| # Generate summary | |
| if [ -f coverage.info ]; then | |
| lcov --list coverage.info >> coverage_summary.txt 2>/dev/null || true | |
| # Extract coverage percentage | |
| COVERAGE=$(lcov --summary coverage.info 2>&1 | grep "lines" | awk '{print $2}' || echo "unknown") | |
| echo "" >> coverage_summary.txt | |
| echo "Total line coverage: $COVERAGE" >> coverage_summary.txt | |
| # Generate HTML report | |
| genhtml coverage.info --output-directory coverage_html 2>/dev/null || true | |
| else | |
| echo "Coverage data not available (kernel code may not support gcov)" >> coverage_summary.txt | |
| echo "Note: Freestanding kernel code requires special handling for coverage" >> coverage_summary.txt | |
| fi | |
| cat coverage_summary.txt | |
| - name: Upload coverage report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: code-coverage | |
| path: | | |
| coverage_summary.txt | |
| coverage.info | |
| coverage_html/ | |
| retention-days: 30 | |
| if-no-files-found: warn | |
| quantum-tests: | |
| name: Quantum Component Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 python3 python3-pip nasm | |
| pip3 install qiskit --break-system-packages || true # Quantum simulator (optional) | |
| - name: Build kernel | |
| run: make clean && make | |
| - name: Test quantum components | |
| run: | | |
| echo "=== Quantum Component Tests ===" > quantum_test_results.txt | |
| QUANTUM_FAILURES=0 | |
| # Check quantum headers exist | |
| echo "Checking quantum headers..." >> quantum_test_results.txt | |
| if [ -f kernel/include/quantum_types.h ]; then | |
| echo " [PASS] quantum_types.h found" >> quantum_test_results.txt | |
| else | |
| echo " [FAIL] quantum_types.h missing" >> quantum_test_results.txt | |
| QUANTUM_FAILURES=$((QUANTUM_FAILURES + 1)) | |
| fi | |
| # Check for quantum-related source files | |
| echo "" >> quantum_test_results.txt | |
| echo "Checking quantum source files..." >> quantum_test_results.txt | |
| QUANTUM_SOURCES=$(find . -name "*quantum*.c" -o -name "*qubit*.c" | wc -l) | |
| echo " Found $QUANTUM_SOURCES quantum-related source files" >> quantum_test_results.txt | |
| # Verify quantum types are properly defined | |
| echo "" >> quantum_test_results.txt | |
| echo "Checking quantum type definitions..." >> quantum_test_results.txt | |
| if grep -q "typedef.*qubit" kernel/include/quantum_types.h 2>/dev/null; then | |
| echo " [PASS] Qubit type defined" >> quantum_test_results.txt | |
| else | |
| echo " [WARN] Qubit type not found" >> quantum_test_results.txt | |
| fi | |
| if grep -q "typedef.*quantum_state" kernel/include/quantum_types.h 2>/dev/null; then | |
| echo " [PASS] Quantum state type defined" >> quantum_test_results.txt | |
| else | |
| echo " [WARN] Quantum state type not found" >> quantum_test_results.txt | |
| fi | |
| echo "" >> quantum_test_results.txt | |
| if [ $QUANTUM_FAILURES -gt 0 ]; then | |
| echo "Quantum tests: $QUANTUM_FAILURES failures" >> quantum_test_results.txt | |
| cat quantum_test_results.txt | |
| exit 1 | |
| fi | |
| echo "Quantum component tests PASSED" >> quantum_test_results.txt | |
| cat quantum_test_results.txt | |
| - name: Upload quantum test results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: quantum-test-results | |
| path: quantum_test_results.txt | |
| retention-days: 7 | |
| resonant-experiment: | |
| name: Resonant Scheduler Experiment (SCHED_RESONANT, honest measurement) | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| # The dormant ghostOS resonant scheduler (issue #21) wired behind | |
| # SCHED_RESONANT=1. Default builds never link it; this job keeps the | |
| # experiment reproducible and publishes its honest verdict (it loses to | |
| # round-robin) on every run. Not a required check — a negative result. | |
| continue-on-error: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm | |
| - name: Build + boot with the resonant scheduler and print the comparison | |
| run: make ci-smoke-resonant | |
| qseed-handoff: | |
| name: qseed Handoff + errno-collision gate (SYS_QSEED provenance) | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| # Single-sourced `make ci-smoke-qseed` (mirrors the mcp-gate/society-gate | |
| # convention). Boots with the seed FFFFFFFFFFFFFFFC — deliberately the value | |
| # that collides with the EPERM (-4) errno sentinel — and proves end to end: | |
| # (1) the kernel accepts the qseed handoff and ghostd traces its noise to it, | |
| # (2) paradoxd still resolves under a qseed, and (3) the qsh `qseed` command | |
| # reports the real seed instead of misreading it as a denial (#167). The | |
| # Integration job's inline Test 1d only ever booted DEADBEEF, so none of the | |
| # -4 collision path was enforced in CI before. | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm | |
| - name: Boot a qseed handoff and assert provenance + the errno-collision gate | |
| run: make ci-smoke-qseed | |
| quiet-boot: | |
| name: Quiet-Boot Test (clean interactive console) | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| # Boot with `-append quiet` and prove the interactive console is CLEAN — the | |
| # periodic timer-tick heartbeat and the demo services' steady-state chatter | |
| # are silenced — WHILE the shell still comes up and answers 'help'. The | |
| # default boot (which keeps all that output, and whose gates depend on it) | |
| # is unchanged; quiet is strictly opt-in. | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm | |
| - name: Prove a quiet boot yields a clean, usable console | |
| run: make ci-smoke-quiet | |
| real-hw-boot: | |
| name: Real-Hardware Boot Path (GRUB ISO + PS/2 keyboard — epic #101) | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| # Two gates for the serial-less laptop path: (1) boot the GRUB-built ISO | |
| # with -cdrom — the real bootloader handoff, not QEMU's -kernel shortcut — | |
| # to the shell + a citizen gate, with the VGA screen console active; | |
| # (2) drive qsh entirely through PS/2 scancodes injected via the QEMU | |
| # monitor, proving the i8042/IRQ1 input path a laptop keyboard uses. | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm grub-pc-bin xorriso mtools | |
| - name: Boot the GRUB ISO to the shell (console image, VGA text) | |
| run: make ci-smoke-iso | |
| - name: Type into qsh via PS/2 scancodes | |
| run: make ci-smoke-kbd | |
| - name: Boot with no COM1 UART at all (the serial-less laptop shape) | |
| run: make ci-smoke-noserial | |
| - name: Assert ring-3 output is visible on the VGA screen | |
| run: make ci-smoke-screen | |
| - name: Every framebuffer banner character has a glyph | |
| run: python3 scripts/check_fb_font.py | |
| persistence: | |
| name: Persistence Test (two boots, one disk — epic #71) | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| # The storage capstone: boot the SAME ATA disk image twice; boot 1 writes a | |
| # file to the RAM overlay and syncs it to disk, boot 2 restores it and reads | |
| # the content back. A REQUIRED gate — persistence is a core-OS claim, and the | |
| # two-boot design makes a spurious green impossible (fresh image per run, | |
| # content checked only in boot 2's log which never types it). | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm | |
| - name: Prove persistence across reboots | |
| run: make ci-smoke-disk | |
| quantum-gateway: | |
| name: Quantum Gateway Cross-Oracle (exact engine vs PennyLane — epic #149) | |
| runs-on: ubuntu-latest | |
| # Phase 2 of the quantum stack: the host gateway dispatches the SAME opaque | |
| # circuit wire format the in-OS broker uses to PennyLane lightning.qubit, | |
| # and this gate asserts the two independent engines — the freestanding exact | |
| # integer simulator (qsv_mirror) and Xanadu's C++ float state vector — agree | |
| # to 1e-9 on Bell/GHZ/Grover, with a teeth check that a wrong circuit | |
| # DISAGREES. Hermetic (pinned wheels); no OS boot needed. | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install pinned PennyLane + Lightning | |
| run: pip install "pennylane==0.45.1" "pennylane-lightning==0.45.0" "numpy>=2.0" | |
| - name: Cross-oracle (exact integer engine vs lightning.qubit) | |
| run: python scripts/test_qsv_oracle.py | |
| quantum-com2: | |
| name: COM2 Quantum-Submit (host → broker → qpud over the wire — epic #149 B1) | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| # Phase 2 transport: a host agent frames an opaque circuit over the attested | |
| # COM2 bridge, swarm_svc forwards it to the SYS_QPU broker, qpud runs it, and | |
| # the exact result returns over a wire the host never typed. PennyLane is | |
| # installed so the cross-oracle leg (OS wire result vs lightning.qubit) runs. | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Install pinned PennyLane + Lightning (for the cross-oracle leg) | |
| run: pip install "pennylane==0.45.1" "pennylane-lightning==0.45.0" "numpy>=2.0" | |
| - name: Prove host→broker→qpud over COM2 | |
| run: make ci-smoke-qsubmit | |
| networking: | |
| name: Networking Test (rtl8139 + user-net ARP — epic #73) | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| # Boot with an rtl8139 NIC on QEMU's user-mode network and prove the link | |
| # layer end to end: SLIRP's gateway (10.0.2.2) always answers ARP, so an | |
| # ARP request that gets a reply exercises PCI enumeration, the driver, the | |
| # TX path, the RX interrupt, and ARP parsing. A required gate. | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| # python3 + curl drive the hermetic TCP test's loopback HTTP server | |
| # (epic #82); installed explicitly rather than relying on the runner | |
| # image's preinstall. | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm python3 curl | |
| - name: ARP-resolve the SLIRP gateway through the rtl8139 driver | |
| run: make ci-smoke-net | |
| - name: Fetch a web page over the ring-3 TCP client (epic #82) | |
| run: make ci-smoke-http | |
| - name: Fetch the live status page FROM QuantumOS (TCP server + httpd, epic #98) | |
| run: make ci-smoke-httpd | |
| - name: Two guests exchange UDP over a raw L2 (static IP + ARP responder, epic #97) | |
| run: make ci-smoke-2net | |
| - name: Two kernels couple their oscillator fields over UDP (epic #97) | |
| run: make ci-smoke-fieldsync | |
| integration: | |
| name: Integration Tests | |
| runs-on: ubuntu-latest | |
| needs: [build] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y build-essential qemu-system-x86 nasm | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: quantumos-build-x86_64-debug | |
| path: build-artifacts/ | |
| - name: Prepare build directory | |
| run: | | |
| mkdir -p build/x86_64 | |
| cp -r build-artifacts/* build/ 2>/dev/null || cp -r build-artifacts/* build/x86_64/ 2>/dev/null || true | |
| find build/ -name "*.elf" -exec ls -la {} \; | |
| - name: Integration tests | |
| run: | | |
| echo "=== Integration Tests ===" > integration_results.txt | |
| INTEGRATION_FAILURES=0 | |
| # Find the kernel ELF | |
| KERNEL_ELF=$(find build/ -name "kernel.elf32" | head -1) | |
| if [ -z "$KERNEL_ELF" ]; then | |
| echo "[FAIL] Kernel ELF not found in artifacts" >> integration_results.txt | |
| cat integration_results.txt | |
| exit 1 | |
| fi | |
| echo "Using kernel: $KERNEL_ELF" >> integration_results.txt | |
| # Test 1: Kernel boot sequence | |
| echo "" >> integration_results.txt | |
| echo "Test 1: Kernel boot sequence..." >> integration_results.txt | |
| timeout 15s qemu-system-x86_64 -kernel "$KERNEL_ELF" \ | |
| -serial stdio -m 128M -display none -no-reboot 2>&1 | tee boot_output.txt || true | |
| if grep -q "QuantumOS ready" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] Kernel booted to idle loop (QuantumOS ready)" >> integration_results.txt | |
| else | |
| echo " [FAIL] Kernel did not reach 'QuantumOS ready'" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| # Test 1b: ghostOS phase-1 gate — the ring-3 ghostd service must | |
| # recall all three noisy probes to their stored patterns (issue #48) | |
| echo "" >> integration_results.txt | |
| echo "Test 1b: ghostd associative-memory self-test..." >> integration_results.txt | |
| if grep -q "GHOSTD: 3/3 RECALL OK" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] ghostd recalled 3/3 patterns (GHOSTD: 3/3 RECALL OK)" >> integration_results.txt | |
| else | |
| echo " [FAIL] ghostd self-test gate missing (GHOSTD: 3/3 RECALL OK)" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| # Test 1c: ghostOS phase-2 honesty gate (issue #49) — this seedless | |
| # boot must name the noise source as a plain PRNG and NEVER claim | |
| # quantum provenance, and the capless SYS_QRAND caller must be denied. | |
| echo "" >> integration_results.txt | |
| echo "Test 1c: ghostd noise-source honesty + QRAND capability gate..." >> integration_results.txt | |
| if grep -q "GHOSTD: noise source = prng (no qseed)" boot_output.txt 2>/dev/null \ | |
| && ! grep -q "GHOSTD: noise source = qseed-derived" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] ghostd reported 'prng (no qseed)' with no false quantum claim" >> integration_results.txt | |
| else | |
| echo " [FAIL] ghostd noise-source honesty gate failed on a seedless boot" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| if grep -q "QRAND: capless caller denied (EPERM)" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] capless SYS_QRAND denied (QRAND: capless caller denied)" >> integration_results.txt | |
| else | |
| echo " [FAIL] capless SYS_QRAND was not denied" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| # Test 1d: ghostOS phase-2 qseed handoff (issue #49) — boot WITH a | |
| # quantum-entropy seed on the kernel command line; the kernel must | |
| # echo it accepted the handoff AND ghostd must trace its noise to it. | |
| echo "" >> integration_results.txt | |
| echo "Test 1d: qseed handoff -> ghostd quantum noise provenance..." >> integration_results.txt | |
| timeout 15s qemu-system-x86_64 -kernel "$KERNEL_ELF" \ | |
| -append "qseed=DEADBEEFCAFEBABE" \ | |
| -serial stdio -m 128M -display none -no-reboot 2>&1 | tee boot_output_qseed.txt || true | |
| if grep -q "Boot entropy accepted from cmdline (qseed=)" boot_output_qseed.txt 2>/dev/null \ | |
| && grep -q "GHOSTD: noise source = qseed-derived" boot_output_qseed.txt 2>/dev/null \ | |
| && grep -q "GHOSTD: 3/3 RECALL OK" boot_output_qseed.txt 2>/dev/null; then | |
| echo " [PASS] qseed accepted + ghostd noise = qseed-derived + 3/3 recall" >> integration_results.txt | |
| else | |
| echo " [FAIL] qseed handoff did not reach ghostd as quantum provenance" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| # Test 1e: ghostOS phase-3 gate (issue #50) — paradoxd must resolve the | |
| # canned contradiction (deterministic RESOLVED line), deny an | |
| # unauthorised targeted send, and demonstrate the ghostd field coupling | |
| # by transitioning its CONVERGENT<->DIVERGENT phase machine gated on R. | |
| echo "" >> integration_results.txt | |
| echo "Test 1e: paradoxd resolution + capability + ghostd coupling..." >> integration_results.txt | |
| if grep -q "PARADOXD: RESOLVED" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] paradoxd resolved the contradiction (PARADOXD: RESOLVED)" >> integration_results.txt | |
| else | |
| echo " [FAIL] paradoxd resolution gate missing (PARADOXD: RESOLVED)" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| if grep -q "PARADOXD: capless send denied (EPERM)" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] unauthorised send denied (PARADOXD: capless send denied)" >> integration_results.txt | |
| else | |
| echo " [FAIL] unauthorised send was not denied" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| if grep -q "PARADOXD: phase ->" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] paradoxd/ghostd field coupling drove a phase transition" >> integration_results.txt | |
| else | |
| echo " [FAIL] no phase transition (paradoxd/ghostd coupling)" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| # Test 1f: ghostd phase-4 device gate (issue #51) — the capless | |
| # ghost-test must be denied SYS_COM2 (only swarm_svc holds the COM2 | |
| # device capability). | |
| echo "" >> integration_results.txt | |
| echo "Test 1f: COM2 device capability gate..." >> integration_results.txt | |
| if grep -q "COM2: capless caller denied (EPERM)" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] capless SYS_COM2 denied (COM2: capless caller denied)" >> integration_results.txt | |
| else | |
| echo " [FAIL] capless SYS_COM2 was not denied" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| # Test 1g: ghostd phase-4 swarm bridge (issue #51) — boot with COM2 | |
| # routed to a file and verify the ring-3 swarm_svc's Lamport-signed boot | |
| # attestation with the host-side verifier, in BOTH modes: seedless | |
| # (qseed=none) and with a qseed handoff (attested qseed == cmdline). | |
| echo "" >> integration_results.txt | |
| echo "Test 1g: swarm bridge Lamport-signed boot attestation..." >> integration_results.txt | |
| timeout 15s qemu-system-x86_64 -kernel "$KERNEL_ELF" \ | |
| -serial stdio -serial file:com2_seedless.bin \ | |
| -m 128M -display none -no-reboot 2>&1 | tee boot_output_swarm.txt || true | |
| if grep -q "SWARM: boot attestation emitted" boot_output_swarm.txt 2>/dev/null; then | |
| echo " [PASS] swarm console gate present (SWARM: boot attestation emitted)" >> integration_results.txt | |
| else | |
| echo " [FAIL] swarm console gate missing" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| if python3 scripts/verify_attestation.py com2_seedless.bin --qseed none >> integration_results.txt 2>&1; then | |
| echo " [PASS] seedless boot attestation verified (qseed=none)" >> integration_results.txt | |
| else | |
| echo " [FAIL] seedless boot attestation did not verify" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| timeout 15s qemu-system-x86_64 -kernel "$KERNEL_ELF" \ | |
| -append "qseed=DEADBEEFCAFEBABE" \ | |
| -serial stdio -serial file:com2_qseed.bin \ | |
| -m 128M -display none -no-reboot 2>&1 | tee boot_output_swarm_qseed.txt || true | |
| if python3 scripts/verify_attestation.py com2_qseed.bin --qseed DEADBEEFCAFEBABE >> integration_results.txt 2>&1; then | |
| echo " [PASS] qseed boot attestation verified (attested qseed == cmdline)" >> integration_results.txt | |
| else | |
| echo " [FAIL] qseed boot attestation did not verify" >> integration_results.txt | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| fi | |
| # Test 1h: interactive shell session (epic #62 phase 1, issue #63) — | |
| # pipe a scripted command session into the serial console. The ring-3 | |
| # qsh service (sole holder of the console capability) must execute it: | |
| # the first command arrives byte-intact across the boot handoff, ps | |
| # shows the shell observing ITSELF running, free/uptime answer with | |
| # live kernel stats, ghost fetches ghostd's field status over | |
| # capability IPC, qrand draws from the shell's declared quantum-pool | |
| # cap, the capless caller is denied SYS_CONS, and after `exit` the | |
| # watchdog restarts the shell (reborn banner). | |
| echo "" >> integration_results.txt | |
| echo "Test 1h: interactive shell session (qsh)..." >> integration_results.txt | |
| ( printf 'help\nps\nfree\nuptime\ndate\nghost\nqrand\nls\ncat /docs/hello.txt\nrun /bin/hello\nrun /bin/args alpha quantumos\nrun /bin/libqtest\nrun /bin/consciousnessd\nrun /bin/qtop\nrun /bin/life\nimprint the cat sat on the mat\nimprint pure quantum wave dynamics\nimprint hello little world\nrecall the cxt sxt on thx mxt\nfieldtest\nwrite /data/note ramfs-works\nls /data\nrm /data/note\nsync\naudit\nmanifest\nexit\n'; sleep 20 ) | \ | |
| timeout 18s qemu-system-x86_64 -kernel "$KERNEL_ELF" \ | |
| -serial stdio -m 128M -display none -no-reboot 2>&1 | tee boot_output_shell.txt || true | |
| SHELL_GATES="QSH: QuantumOS interactive shell ready|qsh commands:|qsh RUNNING|MEM: heap free=|qsh: uptime |qsh: ghost R=|qsh: qrand |Welcome to QuantumOS|FS: etc/motd|The initrd is real|HELLO: greetings from /bin/hello|exited (code 42)|ARGS: argc=3|ARGS: argv[1]=alpha|ARGS: argv[2]=quantumos|qsh: wrote |FS: data/note|qsh: removed|FSW: capless caller denied (EPERM)|qsh: sync failed (no disk)|SPAWN: capless caller denied (EPERM)|CONS: capless caller denied (EPERM)|QSH: reborn|LIBQ: self-test OK|LIBQ printf d=-7 u=42 x=beef s=ok|consciousnessd: CONSCIOUSNESS EMERGED|kannakad: RESONANCE VERIFIED|quantumd: QUANTUM VERIFIED|qtop: DASHBOARD RENDERED|LIFE: glider intact after 16 generations (live=5)|FIELD: imprinted slot 2|FIELD: winner=\"the cat sat on the mat\" slot=0 score=|FIELD: cross-region denied (EPERM)|FIELD: empty-probe ok (n=0)|FIELD: capless imprint denied (EPERM)|FIELD: capless recall denied (EPERM)|manifest self-test: PASS (MDENY recorded)|QUOTA ENFORCED pid=|AUDIT: seq=|MANIFEST: pid=|DELEG ISSUED sub=|DELEG ENFORCED pid=|DELEG REVOKED pid=|CPUKILL: pid=|QSV: grover3 h=15 amp=176 p=121/128 norm=OK|QSV: PROOF COMPLETE|QPU: bell via broker job=|QPU: grover3 via broker job=|QPU: quota ENFORCED (third submit refused)|QPU: capless submit denied (EPERM)" | |
| echo "$SHELL_GATES" | tr '|' '\n' | while IFS= read -r gate; do | |
| if grep -qF "$gate" boot_output_shell.txt 2>/dev/null; then | |
| echo " [PASS] shell gate present: $gate" >> integration_results.txt | |
| else | |
| echo " [FAIL] shell gate missing: $gate" >> integration_results.txt | |
| echo "SHELL_GATE_FAILED" >> shell_gate_failures.txt | |
| fi | |
| done | |
| if [ -f shell_gate_failures.txt ]; then | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + $(wc -l < shell_gate_failures.txt))) | |
| fi | |
| # Negative gate (epic #135): the spawn quota must be ENFORCED, never | |
| # broken. The SHELL_GATES loop above is presence-only and CANNOT express | |
| # absence, so this is a dedicated block — quota-test prints "QUOTA | |
| # BROKEN <detail>" only if the second over-quota spawn was NOT refused. | |
| if grep -qF "QUOTA BROKEN" boot_output_shell.txt 2>/dev/null; then | |
| echo " [FAIL] spawn quota NOT enforced (QUOTA BROKEN present)" >> integration_results.txt | |
| grep -F "QUOTA BROKEN" boot_output_shell.txt >> integration_results.txt || true | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| else | |
| echo " [PASS] spawn quota enforced (no QUOTA BROKEN)" >> integration_results.txt | |
| fi | |
| # Negative gate (epic #137): capability delegation must be enforced. The | |
| # sub-agent prints "DELEG BROKEN <detail>" only if the narrowed cap did | |
| # not behave (recall failed, imprint was allowed, or the cascade-revoke | |
| # was not observed). Presence of the positive DELEG markers is checked | |
| # in SHELL_GATES above; this dedicated block asserts the absence. | |
| if grep -qF "DELEG BROKEN" boot_output_shell.txt 2>/dev/null; then | |
| echo " [FAIL] capability delegation NOT enforced (DELEG BROKEN present)" >> integration_results.txt | |
| grep -F "DELEG BROKEN" boot_output_shell.txt >> integration_results.txt || true | |
| INTEGRATION_FAILURES=$((INTEGRATION_FAILURES + 1)) | |
| else | |
| echo " [PASS] capability delegation enforced (no DELEG BROKEN)" >> integration_results.txt | |
| fi | |
| # Test 2: Check for kernel panic or errors | |
| echo "" >> integration_results.txt | |
| echo "Test 2: Checking for kernel panics..." >> integration_results.txt | |
| if grep -qi "panic\|error\|fault" boot_output.txt 2>/dev/null; then | |
| echo " [WARN] Potential error messages in boot output" >> integration_results.txt | |
| grep -i "panic\|error\|fault" boot_output.txt >> integration_results.txt || true | |
| else | |
| echo " [PASS] No panic/error messages detected" >> integration_results.txt | |
| fi | |
| # Test 3: Memory initialization | |
| echo "" >> integration_results.txt | |
| echo "Test 3: Memory initialization..." >> integration_results.txt | |
| if grep -qi "memory\|heap\|page" boot_output.txt 2>/dev/null; then | |
| echo " [PASS] Memory-related output detected" >> integration_results.txt | |
| else | |
| echo " [INFO] No memory initialization output (may be normal)" >> integration_results.txt | |
| fi | |
| echo "" >> integration_results.txt | |
| echo "Integration tests completed" >> integration_results.txt | |
| cat integration_results.txt | |
| if [ $INTEGRATION_FAILURES -gt 0 ]; then | |
| exit 1 | |
| fi | |
| # MCP server lifecycle (epics #99, #125): the full agent-facing loop | |
| # (boot -> attested identity -> ghostd status -> imprint/recall -> | |
| # run-with-args -> sysinfo -> qrand -> fs -> fetch -> injection refusal -> | |
| # crypto-tamper refusal -> shutdown) driven through qos_bridge, the same | |
| # code path the FastMCP tools delegate to. Run against the downloaded | |
| # kernel artifact, stdlib-only (no `mcp` package, no pip). Invoked via the | |
| # single-sourced `make ci-smoke-mcp-gate` target so the gate timeout is | |
| # defined in exactly one place (the Makefile), never drifting from a | |
| # duplicated literal here. The gate target has no `kernel` prerequisite, so | |
| # this does not rebuild against the artifact. The test owns and reaps its | |
| # own QEMU (finally + atexit + signal handler + PR_SET_PDEATHSIG). | |
| - name: MCP server lifecycle (epics #99, #125) | |
| run: | | |
| KERNEL_ELF=$(find build/ -name "kernel.elf32" | head -1) | |
| echo "Using kernel: $KERNEL_ELF" | |
| QOS_KERNEL="$KERNEL_ELF" make ci-smoke-mcp-gate | |
| # Agent society (epic #131): two attested QuantumOS VMs coupled into one | |
| # holographic field, driven through QosSociety (the same code path the | |
| # qos_society_* MCP tools use) — the MCP-driven generalization of the | |
| # ci-smoke-fieldsync two-kernel coupling, with attestation-gated reporting. | |
| # Invoked via the single-sourced make target (timeout lives in the Makefile). | |
| - name: Agent society coupling (epic #131) | |
| run: | | |
| KERNEL_ELF=$(find build/ -name "kernel.elf32" | head -1) | |
| echo "Using kernel: $KERNEL_ELF" | |
| QOS_KERNEL="$KERNEL_ELF" make ci-smoke-society-gate | |
| # N-way society (epic #139): three kernels mean-field-couple on a shared | |
| # mcast L2. Separate single-sourced timeout; the gate FAILS LOUD if host | |
| # multicast is unavailable (never a silent green — a skipped coupling gate | |
| # proves nothing). | |
| - name: N-way society coupling (epic #139) | |
| run: | | |
| KERNEL_ELF=$(find build/ -name "kernel.elf32" | head -1) | |
| echo "Using kernel: $KERNEL_ELF" | |
| QOS_KERNEL="$KERNEL_ELF" make ci-smoke-society3-gate | |
| - name: Upload integration test results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: integration-test-results | |
| path: | | |
| integration_results.txt | |
| boot_output.txt | |
| retention-days: 7 | |
| release: | |
| name: Release Preparation | |
| runs-on: ubuntu-latest | |
| needs: [build, security, code-quality, documentation, integration] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install dependencies | |
| run: | | |
| sudo apt-get update | |
| # mtools: required by grub-mkrescue on ubuntu-latest — without it the | |
| # ISO build fails (kernel.iso is now a hard error, not a silent skip) | |
| sudo apt-get install -y build-essential qemu-system-x86 grub-pc-bin xorriso mtools nasm | |
| - name: Build release | |
| run: | | |
| make clean | |
| make BUILD_TYPE=release | |
| make build/x86_64/kernel.iso | |
| - name: Generate release notes | |
| run: | | |
| echo "# QuantumOS Release $(date +%Y%m%d)" > release_notes.md | |
| echo "" >> release_notes.md | |
| echo "## Build Information" >> release_notes.md | |
| echo "- Build date: $(date)" >> release_notes.md | |
| echo "- Git commit: ${{ github.sha }}" >> release_notes.md | |
| echo "- Kernel size: $(stat -c%s build/x86_64/kernel.elf) bytes" >> release_notes.md | |
| echo "" >> release_notes.md | |
| echo "## Changes" >> release_notes.md | |
| git log --oneline -10 >> release_notes.md || echo "- See commit history" >> release_notes.md | |
| echo "" >> release_notes.md | |
| echo "## Installation" >> release_notes.md | |
| echo "See README.md for installation instructions." >> release_notes.md | |
| - name: Upload release artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-artifacts | |
| path: | | |
| build/x86_64/kernel.elf | |
| build/x86_64/kernel.iso | |
| release_notes.md | |
| retention-days: 90 |