Skip to content

feat: add env var substitution and config file auto-discovery#77

Merged
Aureliolo merged 2 commits intomainfrom
feat/env-var-substitution-config-discovery
Feb 28, 2026
Merged

feat: add env var substitution and config file auto-discovery#77
Aureliolo merged 2 commits intomainfrom
feat/env-var-substitution-config-discovery

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

Closes #76

  • Add ${VAR} / ${VAR:-default} environment variable substitution in config string values (recursive, immutable, after deep-merge, before Pydantic validation)
  • Add discover_config() for auto-discovery of config files from well-known locations (./ai-company.yaml, ./config/ai-company.yaml, ~/.ai-company/config.yaml)
  • Make load_config(config_path=None) trigger auto-discovery (backward-compatible signature change)
  • Export discover_config from ai_company.config

Test plan

  • 13 unit tests for _substitute_env_vars (simple sub, missing var error, defaults, nested dicts, lists, non-strings, multiple vars, partial strings, immutability, passthrough, deep nesting)
  • 7 unit tests for discover_config (all 3 locations, precedence rules, error case, resolved path)
  • 8 integration tests (env vars through load_config/load_config_from_string, discovery wired to load_config(None), override file substitution, backward compat)
  • Lint (ruff), format (ruff), type-check (mypy) all clean
  • 155 config tests pass, 98% coverage

🤖 Generated with Claude Code

Add ${VAR} and ${VAR:-default} substitution in config string values
and auto-discovery of config files from well-known locations when no
explicit path is given.

- _substitute_env_vars(): recursive dict walker resolving env var
  placeholders after deep-merge, before Pydantic validation
- discover_config(): searches ./ai-company.yaml, ./config/ai-company.yaml,
  ~/.ai-company/config.yaml in order
- load_config() now accepts config_path=None to trigger discovery
- 28 new tests (13 substitution, 7 discovery, 8 integration)
Copilot AI review requested due to automatic review settings February 28, 2026 15:38
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 28, 2026

Warning

Rate limit exceeded

@Aureliolo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 54 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between aede13e and 3cc5a44.

📒 Files selected for processing (5)
  • src/ai_company/config/loader.py
  • src/ai_company/observability/config.py
  • tests/unit/config/conftest.py
  • tests/unit/config/test_loader.py
  • tests/unit/observability/test_config.py
📝 Walkthrough

Walkthrough

The pull request introduces environment variable substitution for config values and automatic config file discovery. Configuration values containing ${VAR} or ${VAR:-default} patterns are recursively replaced with environment variables before validation. The load_config() function now accepts optional path and auto-discovers configs from standard locations when no path is provided.

Changes

Cohort / File(s) Summary
Config Public API
src/ai_company/config/__init__.py
Re-exports newly introduced discover_config function to the package's public API surface with updated __all__ and autosummary documentation.
Config Loader Implementation
src/ai_company/config/loader.py
Adds environment variable substitution via _substitute_env_vars() with support for ${VAR} and ${VAR:-default} patterns. Introduces discover_config() to search standard locations (./ai-company.yaml, ./config/ai-company.yaml, ~/.ai-company/config.yaml). Updates load_config() signature to accept optional config_path and perform auto-discovery when None; integrates substitution into the merge flow.
Test Fixtures & Coverage
tests/unit/config/conftest.py, tests/unit/config/test_loader.py
Adds YAML test fixtures for environment variable scenarios (simple, defaults, nested, missing). Provides comprehensive test coverage for substitution logic (nested structures, missing variables, defaults, mutation safety) and discovery behavior (multiple locations, precedence, error handling, integration with load operations).

Sequence Diagram

sequenceDiagram
    participant User as User Code
    participant Loader as load_config()
    participant Discovery as discover_config()
    participant FileSystem as File System
    participant Parser as YAML Parser
    participant Substitution as _substitute_env_vars()
    participant Validator as Pydantic Validator

    User->>Loader: load_config(config_path=None)
    alt config_path is None
        Loader->>Discovery: discover_config()
        Discovery->>FileSystem: check ./ai-company.yaml
        Discovery->>FileSystem: check ./config/ai-company.yaml
        Discovery->>FileSystem: check ~/.ai-company/config.yaml
        FileSystem-->>Discovery: Path found or not found
        Discovery-->>Loader: return Path or raise ConfigFileNotFoundError
    else config_path provided
        Loader->>FileSystem: use provided path
    end
    
    Loader->>FileSystem: read YAML file
    FileSystem-->>Parser: raw YAML content
    Parser->>Parser: merge with defaults & overrides
    Parser-->>Substitution: merged config dict
    Substitution->>Substitution: replace ${VAR} & ${VAR:-default}
    Substitution-->>Validator: substituted config dict
    Validator->>Validator: validate with Pydantic
    Validator-->>Loader: RootConfig object
    Loader-->>User: return RootConfig
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 100.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: environment variable substitution and config file auto-discovery are the core features added in this PR.
Description check ✅ Passed The description clearly relates to the changeset, detailing the implementation of env var substitution, auto-discovery, and the test coverage for both features.
Linked Issues check ✅ Passed The PR implementation fully meets all acceptance criteria from issue #76: env var substitution with ${VAR} and ${VAR:-default} syntax, discover_config() function, load_config(config_path=None) backward-compatible signature, comprehensive unit and integration tests.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the PR objectives: new public API exports, env var substitution logic, config discovery, updated signatures, and corresponding test coverage. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/env-var-substitution-config-discovery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the configuration loading mechanism by introducing robust environment variable substitution and an intelligent auto-discovery feature for configuration files. These additions provide greater flexibility for deploying and managing application settings across different environments, simplifying the setup process for users by reducing the need for explicit path declarations and allowing dynamic value injection.

Highlights

  • Environment Variable Substitution: Implemented recursive environment variable substitution (e.g., ${VAR}, ${VAR:-default}) in configuration string values. This occurs after deep-merging and before Pydantic validation, ensuring configuration flexibility without mutating the original input.
  • Config File Auto-Discovery: Added a new discover_config() function that automatically searches for configuration files in well-known locations: ./ai-company.yaml, ./config/ai-company.yaml, and ~/.ai-company/config.yaml, following a defined precedence.
  • Enhanced load_config Functionality: The load_config() function now supports auto-discovery when config_path is set to None, making it more convenient to load configurations without explicitly specifying a path. This change maintains backward compatibility.
  • Module Export: The new discover_config function has been exported from the ai_company.config module, making it part of the public API.
Changelog
  • src/ai_company/config/init.py
    • Imported discover_config from ai_company.config.loader.
    • Added discover_config to the __all__ export list.
  • src/ai_company/config/loader.py
    • Imported os and re modules for environment variable handling.
    • Defined _ENV_VAR_PATTERN regex for matching environment variables.
    • Defined _CWD_CONFIG_LOCATIONS and _HOME_CONFIG_RELATIVE for config discovery paths.
    • Implemented _substitute_env_vars function to recursively replace environment variables in dictionary values, handling defaults and raising errors for missing variables.
    • Implemented discover_config function to search for configuration files in predefined locations and return the resolved path.
    • Modified load_config to accept None for config_path, triggering discover_config.
    • Integrated _substitute_env_vars into load_config and load_config_from_string to apply substitutions after merging.
    • Updated docstrings for load_config to reflect environment variable substitution and auto-discovery behavior.
  • tests/unit/config/conftest.py
    • Added new YAML string constants (ENV_VAR_SIMPLE_YAML, ENV_VAR_DEFAULT_YAML, ENV_VAR_NESTED_YAML, ENV_VAR_MISSING_YAML) for testing environment variable substitution scenarios.
  • tests/unit/config/test_loader.py
    • Imported Path for file path manipulation in tests.
    • Imported new environment variable YAML constants from conftest.
    • Added a TestSubstituteEnvVars class with 13 unit tests covering various scenarios for environment variable substitution, including simple substitution, missing variables, defaults, nested structures, lists, non-string values, multiple variables, partial strings, immutability, and deep nesting.
    • Added a TestDiscoverConfig class with 7 unit tests for config auto-discovery, covering finding files in current working directory, subdirectories, home directory, precedence rules, error cases, and resolved paths.
    • Added a TestLoadConfigEnvVarIntegration class with 8 integration tests to verify environment variable substitution through load_config and load_config_from_string, including nested configs and override files.
    • Added a TestLoadConfigDiscoveryIntegration class with 3 integration tests to confirm load_config(None) correctly uses auto-discovery and that explicit paths still function as expected.
Activity
  • 13 unit tests for _substitute_env_vars were implemented and passed.
  • 7 unit tests for discover_config were implemented and passed.
  • 8 integration tests covering environment variable substitution and discovery with load_config and load_config_from_string were implemented and passed.
  • Linting (ruff), formatting (ruff), and type-checking (mypy) were performed and passed without issues.
  • All 155 existing config tests passed, maintaining 98% test coverage.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds two config-loader capabilities requested in #76: environment-variable placeholder expansion within YAML config values and automatic discovery of a config file when no explicit path is provided.

Changes:

  • Implement recursive ${VAR} / ${VAR:-default} substitution for string values in merged config data before Pydantic validation.
  • Add discover_config() to locate config files from standard locations and wire it into load_config(config_path=None).
  • Expand unit tests to cover env-var substitution, discovery precedence, and load_config(None) behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/ai_company/config/loader.py Adds env var substitution, config discovery, and updates load_config / load_config_from_string to apply substitution pre-validation.
src/ai_company/config/__init__.py Exports discover_config as part of the public ai_company.config API.
tests/unit/config/test_loader.py Adds tests for _substitute_env_vars, discover_config, and integration coverage through load_config paths.
tests/unit/config/conftest.py Adds YAML fixtures used by the new env-var tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +425 to +426
# 4. Substitute environment variables
merged = _substitute_env_vars(merged, source_file=str(config_path))
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

In load_config, env var substitution errors are always attributed to the primary config_path via source_file=str(config_path). If a placeholder comes from an override file, the resulting ConfigValidationError will misleadingly point at the primary config. Consider passing a more accurate label (e.g., include override paths or use a generic "" source) so missing-var errors don’t misattribute the source file.

Suggested change
# 4. Substitute environment variables
merged = _substitute_env_vars(merged, source_file=str(config_path))
# 4. Substitute environment variables on the fully merged config.
# Use a neutral label so env-var errors aren't misattributed solely
# to the primary config file when they may originate from overrides.
merged = _substitute_env_vars(merged, source_file="<merged config>")

Copilot uses AI. Check for mistakes.
Comment on lines +115 to +118
ENV_VAR_DEFAULT_YAML = """\
company_name: ${COMPANY_NAME:-Fallback Corp}
"""

Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

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

ENV_VAR_DEFAULT_YAML is added here but isn’t referenced anywhere in the test suite. If it’s not needed, removing it would reduce noise; if it is intended, add a test that uses it so the fixture stays meaningful.

Suggested change
ENV_VAR_DEFAULT_YAML = """\
company_name: ${COMPANY_NAME:-Fallback Corp}
"""

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

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces environment variable substitution in configuration values and auto-discovery of configuration files. While these features are valuable, the unrestricted environment variable substitution in _substitute_env_vars poses a significant security risk, potentially allowing attackers to leak sensitive environment variables if configuration is provided by an untrusted source. It is recommended to implement an allow-list for environment variables or provide a mechanism to disable substitution for untrusted inputs. Additionally, to improve debuggability, consider enhancing error reporting for missing environment variables by including the configuration key path in the error message.

Comment on lines +301 to +315
def _resolve_match(match: re.Match[str]) -> str:
var_name = match.group(1)
default = match.group(2)
value = os.environ.get(var_name)
if value is not None:
return value
if default is not None:
return default
msg = (
f"Environment variable '{var_name}' is not set and no default was provided"
)
raise ConfigValidationError(
msg,
locations=(ConfigLocation(file_path=source_file),),
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

security-high high

The _substitute_env_vars function allows unrestricted access to environment variables, posing a significant security risk. If configuration is provided by an untrusted source (e.g., via an API endpoint), an attacker could leak sensitive environment variables (e.g., DB_PASSWORD, API_KEYS, SECRET_KEY) if the resolved configuration is subsequently exposed. To mitigate this, consider implementing an allow-list of permitted environment variable names or providing an option to disable environment variable substitution when processing configuration from untrusted sources. Additionally, the current error reporting for missing environment variables only includes the source_file. Improving debuggability by tracking and including the key path in the ConfigLocation of the error would be beneficial to pinpoint the exact configuration key causing the issue.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ai_company/config/loader.py`:
- Around line 277-327: The nested logic in _substitute_env_vars exceeds the
50-line limit; extract the nested functions into small private helpers to reduce
its size while keeping behavior identical: create a private
_resolve_env_var_match(match: re.Match, source_file: str | None) -> str that
uses os.environ, returns default when provided, and raises ConfigValidationError
with ConfigLocation(file_path=source_file) on missing vars, and create a private
_walk_substitute(node: Any, source_file: str | None) -> Any that handles str
(calling _ENV_VAR_PATTERN.sub with a lambda or functools.partial to call
_resolve_env_var_match), dict, and list recursion; then simplify
_substitute_env_vars to call _walk_substitute(data, source_file) and return the
result, ensuring names _ENV_VAR_PATTERN, ConfigValidationError, and
ConfigLocation are used unchanged so behavior and error messages remain the
same.

In `@tests/unit/config/test_loader.py`:
- Around line 568-569: The classes TestLoadConfigEnvVarIntegration and
TestLoadConfigDiscoveryIntegration are marked with `@pytest.mark.unit` but are
integration tests; change their pytest marker to `@pytest.mark.integration`
(replace `@pytest.mark.unit` above each class definition) so the test metadata
matches the class intent and they run in integration-only suites; alternatively,
if these are truly unit tests, rename the classes to remove the "Integration"
suffix to avoid confusion.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff3a2ba and aede13e.

📒 Files selected for processing (4)
  • src/ai_company/config/__init__.py
  • src/ai_company/config/loader.py
  • tests/unit/config/conftest.py
  • tests/unit/config/test_loader.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Use PEP 649 native lazy annotations instead of from __future__ import annotations (Python 3.14+)
All public functions and classes must include type hints; enforce via mypy strict mode
Use Google-style docstrings on all public classes and functions (enforced by ruff D rules)
Keep functions under 50 lines and files under 800 lines
Create new objects instead of mutating existing ones; enforce immutability patterns
Use Pydantic v2 models (BaseModel, model_validator, ConfigDict) for data validation
Validate at system boundaries (user input, external APIs, config files); never silently swallow errors
Enforce 88-character line length (via ruff configuration)

Files:

  • tests/unit/config/conftest.py
  • src/ai_company/config/__init__.py
  • src/ai_company/config/loader.py
  • tests/unit/config/test_loader.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Mark tests with @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, or @pytest.mark.slow
Maintain minimum 80% code coverage, enforced in CI via pytest-cov
Set test timeout to 30 seconds per test; use asyncio_mode = 'auto' for async tests without manual @pytest.mark.asyncio

Files:

  • tests/unit/config/conftest.py
  • tests/unit/config/test_loader.py
{src/**/*.py,tests/**/*.py}

📄 CodeRabbit inference engine (CLAUDE.md)

Run ruff check and ruff format as pre-commit hooks on all Python files; enforce no commits to main branch

Files:

  • tests/unit/config/conftest.py
  • src/ai_company/config/__init__.py
  • src/ai_company/config/loader.py
  • tests/unit/config/test_loader.py
🧠 Learnings (3)
📚 Learning: 2026-01-24T09:54:56.100Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/instructions/test-files.instructions.md:0-0
Timestamp: 2026-01-24T09:54:56.100Z
Learning: Applies to **/test_*.py : Use appropriate fixture scopes (`function`, `class`, `module`, `session`) and document complex fixtures with docstrings

Applied to files:

  • tests/unit/config/conftest.py
📚 Learning: 2026-01-24T09:54:56.100Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/instructions/test-files.instructions.md:0-0
Timestamp: 2026-01-24T09:54:56.100Z
Learning: Applies to **/tests/conftest.py : Place shared pytest fixtures in `tests/conftest.py`

Applied to files:

  • tests/unit/config/conftest.py
📚 Learning: 2026-01-26T08:59:32.818Z
Learnt from: CR
Repo: Aureliolo/story-factory PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-26T08:59:32.818Z
Learning: Applies to tests/**/*.py : Use pytest fixtures for test setup. Shared fixtures should be in `tests/conftest.py`

Applied to files:

  • tests/unit/config/conftest.py
🧬 Code graph analysis (2)
src/ai_company/config/__init__.py (1)
src/ai_company/config/loader.py (3)
  • discover_config (335-363)
  • load_config (366-436)
  • load_config_from_string (439-468)
src/ai_company/config/loader.py (1)
src/ai_company/config/errors.py (3)
  • ConfigValidationError (70-106)
  • ConfigLocation (7-21)
  • ConfigFileNotFoundError (62-63)
🔇 Additional comments (5)
tests/unit/config/conftest.py (1)

111-134: Good fixture expansion for env-var scenarios.

These YAML samples are clear and cover the key substitution paths (simple/default/nested/missing) without mutating shared state.

src/ai_company/config/loader.py (2)

335-363: Discovery order and failure reporting look correct.

The search precedence and resolved return path are implemented cleanly, and the not-found error includes all searched locations.


407-427: Load flow ordering is correct.

Applying env-var substitution after deep-merge and before validation in both file and string loaders matches the intended behavior.

Also applies to: 462-463

src/ai_company/config/__init__.py (1)

31-35: Public API export update is consistent.

discover_config is correctly re-exported and included in __all__/autosummary.

Also applies to: 58-58

tests/unit/config/test_loader.py (1)

408-555: Great unit coverage for substitution and discovery edge cases.

These tests meaningfully exercise missing/default/env nesting/immutability and discovery precedence/not-found paths.

Comment on lines +568 to +569
@pytest.mark.unit
class TestLoadConfigEnvVarIntegration:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n -C2 'Integration|@pytest\.mark\.(unit|integration)' tests/unit/config/test_loader.py

Repository: Aureliolo/ai-company

Length of output: 1747


Mark integration-level tests with @pytest.mark.integration instead of @pytest.mark.unit.

The classes TestLoadConfigEnvVarIntegration and TestLoadConfigDiscoveryIntegration are labeled as integration tests (in comments and class names) but marked with @pytest.mark.unit. This causes them to be excluded from integration-only test runs. Either rename these classes to remove the "Integration" prefix if they are unit tests, or mark them with @pytest.mark.integration to match their intent.

Affects lines 568–569 and 623–624.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/config/test_loader.py` around lines 568 - 569, The classes
TestLoadConfigEnvVarIntegration and TestLoadConfigDiscoveryIntegration are
marked with `@pytest.mark.unit` but are integration tests; change their pytest
marker to `@pytest.mark.integration` (replace `@pytest.mark.unit` above each class
definition) so the test metadata matches the class intent and they run in
integration-only suites; alternatively, if these are truly unit tests, rename
the classes to remove the "Integration" suffix to avoid confusion.

…t, and Gemini

- Fix 7 failing CI tests: relax LogConfig.log_dir validator to allow
  absolute paths (keeps path-traversal check, removes relative-only
  restriction that broke pytest tmp_path on Linux CI)
- Refactor _substitute_env_vars into 3 focused functions to meet
  the 50-line function limit (extract _resolve_env_var_match and
  _walk_substitute as module-level helpers)
- Rename TestLoadConfigEnvVarIntegration -> TestLoadConfigEnvVar and
  TestLoadConfigDiscoveryIntegration -> TestLoadConfigDiscovery to
  match their @pytest.mark.unit marker
- Fix source_file attribution in load_config: use "<merged config>"
  so env var errors aren't misattributed to the primary config when
  they originate from override files
- Add 3 new unit tests: recursion pinning (single-pass expansion),
  special chars in env values (URL with ?, &, #), source_file in
  error locations
- Add str path coercion test for load_config(str(path))
- Enhance discover_config error test to verify locations tuple has
  all 3 searched paths
- Remove unused ENV_VAR_DEFAULT_YAML fixture constant
@Aureliolo Aureliolo merged commit 7f53832 into main Feb 28, 2026
9 of 10 checks passed
@Aureliolo Aureliolo deleted the feat/env-var-substitution-config-discovery branch February 28, 2026 16:14
Aureliolo added a commit that referenced this pull request Mar 10, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.1](ai-company-v0.1.0...ai-company-v0.1.1)
(2026-03-10)


### Features

* add autonomy levels and approval timeout policies
([#42](#42),
[#126](#126))
([#197](#197))
([eecc25a](eecc25a))
* add CFO cost optimization service with anomaly detection, reports, and
approval decisions
([#186](#186))
([a7fa00b](a7fa00b))
* add code quality toolchain (ruff, mypy, pre-commit, dependabot)
([#63](#63))
([36681a8](36681a8))
* add configurable cost tiers and subscription/quota-aware tracking
([#67](#67))
([#185](#185))
([9baedfa](9baedfa))
* add container packaging, Docker Compose, and CI pipeline
([#269](#269))
([435bdfe](435bdfe)),
closes [#267](#267)
* add coordination error taxonomy classification pipeline
([#146](#146))
([#181](#181))
([70c7480](70c7480))
* add cost-optimized, hierarchical, and auction assignment strategies
([#175](#175))
([ce924fa](ce924fa)),
closes [#173](#173)
* add design specification, license, and project setup
([8669a09](8669a09))
* add env var substitution and config file auto-discovery
([#77](#77))
([7f53832](7f53832))
* add FastestStrategy routing + vendor-agnostic cleanup
([#140](#140))
([09619cb](09619cb)),
closes [#139](#139)
* add HR engine and performance tracking
([#45](#45),
[#47](#47))
([#193](#193))
([2d091ea](2d091ea))
* add issue auto-search and resolution verification to PR review skill
([#119](#119))
([deecc39](deecc39))
* add memory retrieval, ranking, and context injection pipeline
([#41](#41))
([873b0aa](873b0aa))
* add pluggable MemoryBackend protocol with models, config, and events
([#180](#180))
([46cfdd4](46cfdd4))
* add pluggable MemoryBackend protocol with models, config, and events
([#32](#32))
([46cfdd4](46cfdd4))
* add pluggable PersistenceBackend protocol with SQLite implementation
([#36](#36))
([f753779](f753779))
* add progressive trust and promotion/demotion subsystems
([#43](#43),
[#49](#49))
([3a87c08](3a87c08))
* add retry handler, rate limiter, and provider resilience
([#100](#100))
([b890545](b890545))
* add SecOps security agent with rule engine, audit log, and ToolInvoker
integration ([#40](#40))
([83b7b6c](83b7b6c))
* add shared org memory and memory consolidation/archival
([#125](#125),
[#48](#48))
([4a0832b](4a0832b))
* design unified provider interface
([#86](#86))
([3e23d64](3e23d64))
* expand template presets, rosters, and add inheritance
([#80](#80),
[#81](#81),
[#84](#84))
([15a9134](15a9134))
* implement agent runtime state vs immutable config split
([#115](#115))
([4cb1ca5](4cb1ca5))
* implement AgentEngine core orchestrator
([#11](#11))
([#143](#143))
([f2eb73a](f2eb73a))
* implement basic tool system (registry, invocation, results)
([#15](#15))
([c51068b](c51068b))
* implement built-in file system tools
([#18](#18))
([325ef98](325ef98))
* implement communication foundation — message bus, dispatcher, and
messenger ([#157](#157))
([8e71bfd](8e71bfd))
* implement company template system with 7 built-in presets
([#85](#85))
([cbf1496](cbf1496))
* implement conflict resolution protocol
([#122](#122))
([#166](#166))
([e03f9f2](e03f9f2))
* implement core entity and role system models
([#69](#69))
([acf9801](acf9801))
* implement crash recovery with fail-and-reassign strategy
([#149](#149))
([e6e91ed](e6e91ed))
* implement engine extensions — Plan-and-Execute loop and call
categorization
([#134](#134),
[#135](#135))
([#159](#159))
([9b2699f](9b2699f))
* implement enterprise logging system with structlog
([#73](#73))
([2f787e5](2f787e5))
* implement graceful shutdown with cooperative timeout strategy
([#130](#130))
([6592515](6592515))
* implement hierarchical delegation and loop prevention
([#12](#12),
[#17](#17))
([6be60b6](6be60b6))
* implement LiteLLM driver and provider registry
([#88](#88))
([ae3f18b](ae3f18b)),
closes [#4](#4)
* implement LLM decomposition strategy and workspace isolation
([#174](#174))
([aa0eefe](aa0eefe))
* implement meeting protocol system
([#123](#123))
([ee7caca](ee7caca))
* implement message and communication domain models
([#74](#74))
([560a5d2](560a5d2))
* implement model routing engine
([#99](#99))
([d3c250b](d3c250b))
* implement parallel agent execution
([#22](#22))
([#161](#161))
([65940b3](65940b3))
* implement per-call cost tracking service
([#7](#7))
([#102](#102))
([c4f1f1c](c4f1f1c))
* implement personality injection and system prompt construction
([#105](#105))
([934dd85](934dd85))
* implement single-task execution lifecycle
([#21](#21))
([#144](#144))
([c7e64e4](c7e64e4))
* implement subprocess sandbox for tool execution isolation
([#131](#131))
([#153](#153))
([3c8394e](3c8394e))
* implement task assignment subsystem with pluggable strategies
([#172](#172))
([c7f1b26](c7f1b26)),
closes [#26](#26)
[#30](#30)
* implement task decomposition and routing engine
([#14](#14))
([9c7fb52](9c7fb52))
* implement Task, Project, Artifact, Budget, and Cost domain models
([#71](#71))
([81eabf1](81eabf1))
* implement tool permission checking
([#16](#16))
([833c190](833c190))
* implement YAML config loader with Pydantic validation
([#59](#59))
([ff3a2ba](ff3a2ba))
* implement YAML config loader with Pydantic validation
([#75](#75))
([ff3a2ba](ff3a2ba))
* initialize project with uv, hatchling, and src layout
([39005f9](39005f9))
* initialize project with uv, hatchling, and src layout
([#62](#62))
([39005f9](39005f9))
* Litestar REST API, WebSocket feed, and approval queue (M6)
([#189](#189))
([29fcd08](29fcd08))
* make TokenUsage.total_tokens a computed field
([#118](#118))
([c0bab18](c0bab18)),
closes [#109](#109)
* parallel tool execution in ToolInvoker.invoke_all
([#137](#137))
([58517ee](58517ee))
* testing framework, CI pipeline, and M0 gap fixes
([#64](#64))
([f581749](f581749))
* wire all modules into observability system
([#97](#97))
([f7a0617](f7a0617))


### Bug Fixes

* address Greptile post-merge review findings from PRs
[#170](https://github.com/Aureliolo/ai-company/issues/170)-[#175](https://github.com/Aureliolo/ai-company/issues/175)
([#176](#176))
([c5ca929](c5ca929))
* address post-merge review feedback from PRs
[#164](https://github.com/Aureliolo/ai-company/issues/164)-[#167](https://github.com/Aureliolo/ai-company/issues/167)
([#170](#170))
([3bf897a](3bf897a)),
closes [#169](#169)
* enforce strict mypy on test files
([#89](#89))
([aeeff8c](aeeff8c))
* harden Docker sandbox, MCP bridge, and code runner
([#50](#50),
[#53](#53))
([d5e1b6e](d5e1b6e))
* harden git tools security + code quality improvements
([#150](#150))
([000a325](000a325))
* harden subprocess cleanup, env filtering, and shutdown resilience
([#155](#155))
([d1fe1fb](d1fe1fb))
* incorporate post-merge feedback + pre-PR review fixes
([#164](#164))
([c02832a](c02832a))
* pre-PR review fixes for post-merge findings
([#183](#183))
([26b3108](26b3108))
* strengthen immutability for BaseTool schema and ToolInvoker boundaries
([#117](#117))
([7e5e861](7e5e861))


### Performance

* harden non-inferable principle implementation
([#195](#195))
([02b5f4e](02b5f4e)),
closes [#188](#188)


### Refactoring

* adopt NotBlankStr across all models
([#108](#108))
([#120](#120))
([ef89b90](ef89b90))
* extract _SpendingTotals base class from spending summary models
([#111](#111))
([2f39c1b](2f39c1b))
* harden BudgetEnforcer with error handling, validation extraction, and
review fixes
([#182](#182))
([c107bf9](c107bf9))
* harden personality profiles, department validation, and template
rendering ([#158](#158))
([10b2299](10b2299))
* pre-PR review improvements for ExecutionLoop + ReAct loop
([#124](#124))
([8dfb3c0](8dfb3c0))
* split events.py into per-domain event modules
([#136](#136))
([e9cba89](e9cba89))


### Documentation

* add ADR-001 memory layer evaluation and selection
([#178](#178))
([db3026f](db3026f)),
closes [#39](#39)
* add agent scaling research findings to DESIGN_SPEC
([#145](#145))
([57e487b](57e487b))
* add CLAUDE.md, contributing guide, and dev documentation
([#65](#65))
([55c1025](55c1025)),
closes [#54](#54)
* add crash recovery, sandboxing, analytics, and testing decisions
([#127](#127))
([5c11595](5c11595))
* address external review feedback with MVP scope and new protocols
([#128](#128))
([3b30b9a](3b30b9a))
* expand design spec with pluggable strategy protocols
([#121](#121))
([6832db6](6832db6))
* finalize 23 design decisions (ADR-002)
([#190](#190))
([8c39742](8c39742))
* update project docs for M2.5 conventions and add docs-consistency
review agent
([#114](#114))
([99766ee](99766ee))


### Tests

* add e2e single agent integration tests
([#24](#24))
([#156](#156))
([f566fb4](f566fb4))
* add provider adapter integration tests
([#90](#90))
([40a61f4](40a61f4))


### CI/CD

* add Release Please for automated versioning and GitHub Releases
([#278](#278))
([a488758](a488758))
* bump actions/checkout from 4 to 6
([#95](#95))
([1897247](1897247))
* bump actions/upload-artifact from 4 to 7
([#94](#94))
([27b1517](27b1517))
* harden CI/CD pipeline
([#92](#92))
([ce4693c](ce4693c))
* split vulnerability scans into critical-fail and high-warn tiers
([#277](#277))
([aba48af](aba48af))


### Maintenance

* add /worktree skill for parallel worktree management
([#171](#171))
([951e337](951e337))
* add design spec context loading to research-link skill
([8ef9685](8ef9685))
* add post-merge-cleanup skill
([#70](#70))
([f913705](f913705))
* add pre-pr-review skill and update CLAUDE.md
([#103](#103))
([92e9023](92e9023))
* add research-link skill and rename skill files to SKILL.md
([#101](#101))
([651c577](651c577))
* bump aiosqlite from 0.21.0 to 0.22.1
([#191](#191))
([3274a86](3274a86))
* bump pyyaml from 6.0.2 to 6.0.3 in the minor-and-patch group
([#96](#96))
([0338d0c](0338d0c))
* bump ruff from 0.15.4 to 0.15.5
([a49ee46](a49ee46))
* fix M0 audit items
([#66](#66))
([c7724b5](c7724b5))
* pin setup-uv action to full SHA
([#281](#281))
([4448002](4448002))
* post-audit cleanup — PEP 758, loggers, bug fixes, refactoring, tests,
hookify rules
([#148](#148))
([c57a6a9](c57a6a9))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Aureliolo added a commit that referenced this pull request Mar 11, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.0](v0.0.0...v0.1.0)
(2026-03-11)


### Features

* add autonomy levels and approval timeout policies
([#42](#42),
[#126](#126))
([#197](#197))
([eecc25a](eecc25a))
* add CFO cost optimization service with anomaly detection, reports, and
approval decisions
([#186](#186))
([a7fa00b](a7fa00b))
* add code quality toolchain (ruff, mypy, pre-commit, dependabot)
([#63](#63))
([36681a8](36681a8))
* add configurable cost tiers and subscription/quota-aware tracking
([#67](#67))
([#185](#185))
([9baedfa](9baedfa))
* add container packaging, Docker Compose, and CI pipeline
([#269](#269))
([435bdfe](435bdfe)),
closes [#267](#267)
* add coordination error taxonomy classification pipeline
([#146](#146))
([#181](#181))
([70c7480](70c7480))
* add cost-optimized, hierarchical, and auction assignment strategies
([#175](#175))
([ce924fa](ce924fa)),
closes [#173](#173)
* add design specification, license, and project setup
([8669a09](8669a09))
* add env var substitution and config file auto-discovery
([#77](#77))
([7f53832](7f53832))
* add FastestStrategy routing + vendor-agnostic cleanup
([#140](#140))
([09619cb](09619cb)),
closes [#139](#139)
* add HR engine and performance tracking
([#45](#45),
[#47](#47))
([#193](#193))
([2d091ea](2d091ea))
* add issue auto-search and resolution verification to PR review skill
([#119](#119))
([deecc39](deecc39))
* add mandatory JWT + API key authentication
([#256](#256))
([c279cfe](c279cfe))
* add memory retrieval, ranking, and context injection pipeline
([#41](#41))
([873b0aa](873b0aa))
* add pluggable MemoryBackend protocol with models, config, and events
([#180](#180))
([46cfdd4](46cfdd4))
* add pluggable MemoryBackend protocol with models, config, and events
([#32](#32))
([46cfdd4](46cfdd4))
* add pluggable output scan response policies
([#263](#263))
([b9907e8](b9907e8))
* add pluggable PersistenceBackend protocol with SQLite implementation
([#36](#36))
([f753779](f753779))
* add progressive trust and promotion/demotion subsystems
([#43](#43),
[#49](#49))
([3a87c08](3a87c08))
* add retry handler, rate limiter, and provider resilience
([#100](#100))
([b890545](b890545))
* add SecOps security agent with rule engine, audit log, and ToolInvoker
integration ([#40](#40))
([83b7b6c](83b7b6c))
* add shared org memory and memory consolidation/archival
([#125](#125),
[#48](#48))
([4a0832b](4a0832b))
* design unified provider interface
([#86](#86))
([3e23d64](3e23d64))
* expand template presets, rosters, and add inheritance
([#80](#80),
[#81](#81),
[#84](#84))
([15a9134](15a9134))
* implement agent runtime state vs immutable config split
([#115](#115))
([4cb1ca5](4cb1ca5))
* implement AgentEngine core orchestrator
([#11](#11))
([#143](#143))
([f2eb73a](f2eb73a))
* implement AuditRepository for security audit log persistence
([#279](#279))
([94bc29f](94bc29f))
* implement basic tool system (registry, invocation, results)
([#15](#15))
([c51068b](c51068b))
* implement built-in file system tools
([#18](#18))
([325ef98](325ef98))
* implement communication foundation — message bus, dispatcher, and
messenger ([#157](#157))
([8e71bfd](8e71bfd))
* implement company template system with 7 built-in presets
([#85](#85))
([cbf1496](cbf1496))
* implement conflict resolution protocol
([#122](#122))
([#166](#166))
([e03f9f2](e03f9f2))
* implement core entity and role system models
([#69](#69))
([acf9801](acf9801))
* implement crash recovery with fail-and-reassign strategy
([#149](#149))
([e6e91ed](e6e91ed))
* implement engine extensions — Plan-and-Execute loop and call
categorization
([#134](#134),
[#135](#135))
([#159](#159))
([9b2699f](9b2699f))
* implement enterprise logging system with structlog
([#73](#73))
([2f787e5](2f787e5))
* implement graceful shutdown with cooperative timeout strategy
([#130](#130))
([6592515](6592515))
* implement hierarchical delegation and loop prevention
([#12](#12),
[#17](#17))
([6be60b6](6be60b6))
* implement LiteLLM driver and provider registry
([#88](#88))
([ae3f18b](ae3f18b)),
closes [#4](#4)
* implement LLM decomposition strategy and workspace isolation
([#174](#174))
([aa0eefe](aa0eefe))
* implement meeting protocol system
([#123](#123))
([ee7caca](ee7caca))
* implement message and communication domain models
([#74](#74))
([560a5d2](560a5d2))
* implement model routing engine
([#99](#99))
([d3c250b](d3c250b))
* implement parallel agent execution
([#22](#22))
([#161](#161))
([65940b3](65940b3))
* implement per-call cost tracking service
([#7](#7))
([#102](#102))
([c4f1f1c](c4f1f1c))
* implement personality injection and system prompt construction
([#105](#105))
([934dd85](934dd85))
* implement single-task execution lifecycle
([#21](#21))
([#144](#144))
([c7e64e4](c7e64e4))
* implement subprocess sandbox for tool execution isolation
([#131](#131))
([#153](#153))
([3c8394e](3c8394e))
* implement task assignment subsystem with pluggable strategies
([#172](#172))
([c7f1b26](c7f1b26)),
closes [#26](#26)
[#30](#30)
* implement task decomposition and routing engine
([#14](#14))
([9c7fb52](9c7fb52))
* implement Task, Project, Artifact, Budget, and Cost domain models
([#71](#71))
([81eabf1](81eabf1))
* implement tool permission checking
([#16](#16))
([833c190](833c190))
* implement YAML config loader with Pydantic validation
([#59](#59))
([ff3a2ba](ff3a2ba))
* implement YAML config loader with Pydantic validation
([#75](#75))
([ff3a2ba](ff3a2ba))
* initialize project with uv, hatchling, and src layout
([39005f9](39005f9))
* initialize project with uv, hatchling, and src layout
([#62](#62))
([39005f9](39005f9))
* Litestar REST API, WebSocket feed, and approval queue (M6)
([#189](#189))
([29fcd08](29fcd08))
* make TokenUsage.total_tokens a computed field
([#118](#118))
([c0bab18](c0bab18)),
closes [#109](#109)
* parallel tool execution in ToolInvoker.invoke_all
([#137](#137))
([58517ee](58517ee))
* testing framework, CI pipeline, and M0 gap fixes
([#64](#64))
([f581749](f581749))
* wire all modules into observability system
([#97](#97))
([f7a0617](f7a0617))


### Bug Fixes

* address Greptile post-merge review findings from PRs
[#170](https://github.com/Aureliolo/ai-company/issues/170)-[#175](https://github.com/Aureliolo/ai-company/issues/175)
([#176](#176))
([c5ca929](c5ca929))
* address post-merge review feedback from PRs
[#164](https://github.com/Aureliolo/ai-company/issues/164)-[#167](https://github.com/Aureliolo/ai-company/issues/167)
([#170](#170))
([3bf897a](3bf897a)),
closes [#169](#169)
* enforce strict mypy on test files
([#89](#89))
([aeeff8c](aeeff8c))
* harden Docker sandbox, MCP bridge, and code runner
([#50](#50),
[#53](#53))
([d5e1b6e](d5e1b6e))
* harden git tools security + code quality improvements
([#150](#150))
([000a325](000a325))
* harden subprocess cleanup, env filtering, and shutdown resilience
([#155](#155))
([d1fe1fb](d1fe1fb))
* incorporate post-merge feedback + pre-PR review fixes
([#164](#164))
([c02832a](c02832a))
* pre-PR review fixes for post-merge findings
([#183](#183))
([26b3108](26b3108))
* resolve circular imports, bump litellm, fix release tag format
([#286](#286))
([a6659b5](a6659b5))
* strengthen immutability for BaseTool schema and ToolInvoker boundaries
([#117](#117))
([7e5e861](7e5e861))


### Performance

* harden non-inferable principle implementation
([#195](#195))
([02b5f4e](02b5f4e)),
closes [#188](#188)


### Refactoring

* adopt NotBlankStr across all models
([#108](#108))
([#120](#120))
([ef89b90](ef89b90))
* extract _SpendingTotals base class from spending summary models
([#111](#111))
([2f39c1b](2f39c1b))
* harden BudgetEnforcer with error handling, validation extraction, and
review fixes
([#182](#182))
([c107bf9](c107bf9))
* harden personality profiles, department validation, and template
rendering ([#158](#158))
([10b2299](10b2299))
* pre-PR review improvements for ExecutionLoop + ReAct loop
([#124](#124))
([8dfb3c0](8dfb3c0))
* split events.py into per-domain event modules
([#136](#136))
([e9cba89](e9cba89))


### Documentation

* add ADR-001 memory layer evaluation and selection
([#178](#178))
([db3026f](db3026f)),
closes [#39](#39)
* add agent scaling research findings to DESIGN_SPEC
([#145](#145))
([57e487b](57e487b))
* add CLAUDE.md, contributing guide, and dev documentation
([#65](#65))
([55c1025](55c1025)),
closes [#54](#54)
* add crash recovery, sandboxing, analytics, and testing decisions
([#127](#127))
([5c11595](5c11595))
* address external review feedback with MVP scope and new protocols
([#128](#128))
([3b30b9a](3b30b9a))
* expand design spec with pluggable strategy protocols
([#121](#121))
([6832db6](6832db6))
* finalize 23 design decisions (ADR-002)
([#190](#190))
([8c39742](8c39742))
* update project docs for M2.5 conventions and add docs-consistency
review agent
([#114](#114))
([99766ee](99766ee))


### Tests

* add e2e single agent integration tests
([#24](#24))
([#156](#156))
([f566fb4](f566fb4))
* add provider adapter integration tests
([#90](#90))
([40a61f4](40a61f4))


### CI/CD

* add Release Please for automated versioning and GitHub Releases
([#278](#278))
([a488758](a488758))
* bump actions/checkout from 4 to 6
([#95](#95))
([1897247](1897247))
* bump actions/upload-artifact from 4 to 7
([#94](#94))
([27b1517](27b1517))
* bump anchore/scan-action from 6.5.1 to 7.3.2
([#271](#271))
([80a1c15](80a1c15))
* bump docker/build-push-action from 6.19.2 to 7.0.0
([#273](#273))
([dd0219e](dd0219e))
* bump docker/login-action from 3.7.0 to 4.0.0
([#272](#272))
([33d6238](33d6238))
* bump docker/metadata-action from 5.10.0 to 6.0.0
([#270](#270))
([baee04e](baee04e))
* bump docker/setup-buildx-action from 3.12.0 to 4.0.0
([#274](#274))
([5fc06f7](5fc06f7))
* bump sigstore/cosign-installer from 3.9.1 to 4.1.0
([#275](#275))
([29dd16c](29dd16c))
* harden CI/CD pipeline
([#92](#92))
([ce4693c](ce4693c))
* split vulnerability scans into critical-fail and high-warn tiers
([#277](#277))
([aba48af](aba48af))


### Maintenance

* add /worktree skill for parallel worktree management
([#171](#171))
([951e337](951e337))
* add design spec context loading to research-link skill
([8ef9685](8ef9685))
* add post-merge-cleanup skill
([#70](#70))
([f913705](f913705))
* add pre-pr-review skill and update CLAUDE.md
([#103](#103))
([92e9023](92e9023))
* add research-link skill and rename skill files to SKILL.md
([#101](#101))
([651c577](651c577))
* bump aiosqlite from 0.21.0 to 0.22.1
([#191](#191))
([3274a86](3274a86))
* bump pyyaml from 6.0.2 to 6.0.3 in the minor-and-patch group
([#96](#96))
([0338d0c](0338d0c))
* bump ruff from 0.15.4 to 0.15.5
([a49ee46](a49ee46))
* fix M0 audit items
([#66](#66))
([c7724b5](c7724b5))
* **main:** release ai-company 0.1.1
([#282](#282))
([2f4703d](2f4703d))
* pin setup-uv action to full SHA
([#281](#281))
([4448002](4448002))
* post-audit cleanup — PEP 758, loggers, bug fixes, refactoring, tests,
hookify rules
([#148](#148))
([c57a6a9](c57a6a9))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Signed-off-by: Aurelio <19254254+Aureliolo@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add env var substitution and config file discovery to loader

2 participants