Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- NTNT coding guide sections are sourced from docs/AI_AGENT_GUIDE.md -->
<!-- To update NTNT coding instructions, edit AI_AGENT_GUIDE.md and copy to all agent files -->
<!-- Last synced: 2026-03-09 -->
<!-- Last synced: 2026-03-11 -->

# NTNT Language - GitHub Copilot Instructions

Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
name: Test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
Expand All @@ -39,6 +40,10 @@ jobs:

- name: Run tests
run: cargo test --locked
env:
# macOS aarch64 has smaller default thread stack (512KB vs 8MB on Linux).
# Increase to 8MB to prevent stack overflow in recursive interpreter tests.
RUST_MIN_STACK: 8388608

lint:
name: Lint
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- NTNT coding guide sections are sourced from docs/AI_AGENT_GUIDE.md -->
<!-- To update NTNT coding instructions, edit AI_AGENT_GUIDE.md and copy to all agent files -->
<!-- Last synced: 2026-03-09 -->
<!-- Last synced: 2026-03-11 -->

# NTNT Language - Claude Code Instructions

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ntnt"
version = "0.4.0"
version = "0.4.1"
edition = "2021"
authors = ["NTNT Language Team"]
description = "NTNT (Intent) - A programming language designed for AI-driven development"
Expand Down
2 changes: 1 addition & 1 deletion docs/IAL_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Auto-generated from [ial.toml](ial.toml)** - Do not edit directly.
>
> Last updated: v0.4.0
> Last updated: v0.4.1

IAL is a term rewriting engine that translates natural language assertions into executable tests

Expand Down
2 changes: 1 addition & 1 deletion docs/RUNTIME_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Auto-generated from [runtime.toml](runtime.toml)** - Do not edit directly.
>
> Last updated: v0.4.0
> Last updated: v0.4.1

Runtime configuration, environment variables, and CLI commands for NTNT

Expand Down
2 changes: 1 addition & 1 deletion docs/STDLIB_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Auto-generated from source code doc comments** - Do not edit directly.
>
> Last updated: v0.4.0
> Last updated: v0.4.1

## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion docs/SYNTAX_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> **Auto-generated from [syntax.toml](syntax.toml)** - Do not edit directly.
>
> Last updated: v0.4.0
> Last updated: v0.4.1

## Table of Contents

Expand Down
35 changes: 33 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub enum LintMode {
Strict,
}

#[cfg_attr(test, allow(dead_code))]
fn read_type_mode_from_env() -> TypeMode {
match std::env::var("NTNT_TYPE_MODE").as_deref().unwrap_or("warn") {
"strict" => TypeMode::Strict,
Expand All @@ -69,9 +70,38 @@ pub fn get_type_mode() -> TypeMode {

#[cfg(test)]
pub fn get_type_mode() -> TypeMode {
read_type_mode_from_env()
// In tests, use thread-local override only (no env var reads).
// std::env::var is unsafe to call concurrently with set_var on macOS
// (Rust 1.83+ / POSIX getenv is not thread-safe on all platforms).
TYPE_MODE_OVERRIDE.with(|cell| (*cell.borrow()).unwrap_or(TypeMode::Warn))
Comment on lines 73 to +78
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

get_type_mode() in test builds now uses a thread-local override and does not read NTNT_TYPE_MODE from the environment, but the surrounding documentation (above this function) still describes per-call env reads for tests. Please update the docs/comments to match the new thread-local override behavior so tests don’t attempt to rely on std::env::set_var("NTNT_TYPE_MODE", ...).

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in bd0742c. Doc comment now describes the thread-local override behavior and points to set_test_type_mode.

}

thread_local! {
/// Thread-local override for TypeMode in tests. Avoids `std::env::set_var`
/// which is unsafe in multi-threaded contexts (Rust 1.83+).
static TYPE_MODE_OVERRIDE: RefCell<Option<TypeMode>> = const { RefCell::new(None) };
}

/// Set a thread-local TypeMode override for the current test. Returns a guard
/// that restores `None` on drop. Use instead of `std::env::set_var("NTNT_TYPE_MODE", ...)`.
#[cfg(test)]
pub fn set_test_type_mode(mode: TypeMode) -> TestTypeModeGuard {
TYPE_MODE_OVERRIDE.with(|cell| *cell.borrow_mut() = Some(mode));
TestTypeModeGuard
}

/// RAII guard that clears the thread-local TypeMode override on drop.
#[cfg(test)]
pub struct TestTypeModeGuard;

#[cfg(test)]
impl Drop for TestTypeModeGuard {
fn drop(&mut self) {
TYPE_MODE_OVERRIDE.with(|cell| *cell.borrow_mut() = None);
}
}

#[cfg_attr(test, allow(dead_code))]
fn read_lint_mode_from_env() -> LintMode {
match std::env::var("NTNT_LINT_MODE")
.as_deref()
Expand All @@ -97,7 +127,8 @@ pub fn get_lint_mode() -> LintMode {

#[cfg(test)]
pub fn get_lint_mode() -> LintMode {
read_lint_mode_from_env()
// In tests, always return Default (no env var reads for thread safety).
LintMode::Default
Comment on lines 132 to +135
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

get_lint_mode() in test builds now always returns LintMode::Default, which diverges from the documented behavior elsewhere that test builds re-read NTNT_LINT_MODE each call. If this change is intentional for thread-safety, consider updating the surrounding docs/comments to avoid confusion, or adding a thread-local override similar to TypeMode so lint-mode behavior can still be tested deterministically.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in bd0742c. Doc comment now explains that test builds return LintMode::Default for thread safety, and suggests adding a thread-local override (like TypeMode) if lint-mode testing is needed in the future.

}

use std::cell::RefCell;
Expand Down
Loading