Compare commits

3 Commits
v0.1.0 ... main

6 changed files with 301 additions and 195 deletions

124
README.md
View File

@@ -7,8 +7,11 @@ manage root-owned application state.
## Variables ## Variables
Every invocation requires caller-supplied `audita_backends` and
`audita_llm_proposal_profile`. Neither has a role default.
```yaml ```yaml
audita_version: v0.9.1 audita_version: v2.0.1
audita_repository_url: https://gitea.maximumdirect.net/eric/audita.git audita_repository_url: https://gitea.maximumdirect.net/eric/audita.git
audita_source_dir: /usr/local/src/audita audita_source_dir: /usr/local/src/audita
audita_binary_path: /usr/local/bin/audita audita_binary_path: /usr/local/bin/audita
@@ -20,22 +23,15 @@ audita_config_owner: root
audita_config_group: root audita_config_group: root
audita_config_directory_mode: "0755" audita_config_directory_mode: "0755"
audita_config_file_mode: "0644" audita_config_file_mode: "0644"
audita_config_version: 1 audita_config_version: 2
audita_profiles_dir: null
audita_pipeline_modules: null audita_pipeline_modules: null
audita_output_schema: null audita_output_schema: null
audita_llm_proposal_base_url: null audita_llm_validation_profile: null
audita_llm_proposal_model: null audita_llm_transient_retries: null
audita_llm_proposal_api_key_env: null audita_llm_repair_attempts: null
audita_llm_proposal_timeout: null audita_llm_operation_timeout: null
audita_llm_proposal_max_retries: null audita_validation_max_prompt_tokens: null
audita_llm_validation_base_url: null
audita_llm_validation_model: null
audita_llm_validation_api_key_env: null
audita_llm_validation_timeout: null
audita_llm_validation_max_retries: null
audita_concurrency_total_llm: null
audita_concurrency_proposal_llm: null
audita_concurrency_validation_llm: null
audita_chunking_target_sections: null audita_chunking_target_sections: null
audita_chunking_max_section_tokens: null audita_chunking_max_section_tokens: null
audita_chunking_min_section_tokens: null audita_chunking_min_section_tokens: null
@@ -48,8 +44,10 @@ audita_thresholds_homophones: null
audita_thresholds_spoken_word: null audita_thresholds_spoken_word: null
audita_thresholds_grammar: null audita_thresholds_grammar: null
audita_context_description: null audita_context_description: null
audita_reporting_enabled: null
audita_reporting_stdout_dir: null
audita_diagnostics_debug: null
audita_diagnostics_work_dir: null audita_diagnostics_work_dir: null
audita_diagnostics_retention: null
``` ```
When `audita_manage_build_dependencies` is `false`, Git and the executable When `audita_manage_build_dependencies` is `false`, Git and the executable
@@ -63,25 +61,77 @@ selecting `audita_version`. The role builds Audita directly to
the checkout changes or the binary is absent. The source tree is recursively the checkout changes or the binary is absent. The source tree is recursively
owned by `root:root`; the binary is `root:root` mode `0755`. owned by `root:root`; the binary is `root:root` mode `0755`.
The configuration variables map directly to Audita's versioned YAML fields. The configuration variables map directly to Audita's version-2 YAML fields.
Every optional variable defaults to `null`; null values and their empty parent `audita_backends` and `audita_llm_proposal_profile` are required caller inputs;
sections are omitted, allowing Audita to retain ownership of its built-in the role has no defaults for them. Callers own the selected PromptKit profile
files. Every optional variable defaults to `null`; null values and their empty
parent sections are omitted, allowing Audita to retain ownership of its built-in
defaults. `audita_config_version` always renders as `version` and defaults to defaults. `audita_config_version` always renders as `version` and defaults to
`1`. The role manages `audita_config_path | dirname` and the resulting file `2`. The role manages `audita_config_path | dirname` and the resulting file
with the `audita_config_owner`, `audita_config_group`, with the `audita_config_owner`, `audita_config_group`,
`audita_config_directory_mode`, and `audita_config_file_mode` inputs. The `audita_config_directory_mode`, and `audita_config_file_mode` inputs. The
standalone defaults are `root:root`, directory mode `0755`, and file mode standalone defaults are `root:root`, directory mode `0755`, and file mode
`0644`. Composing roles may override these generic configuration inputs for `0644`. Composing roles may override these generic configuration inputs for
their own deployment policy. their own deployment policy.
LLM API-key settings name environment variables through Backend `credential` values are explicit environment-variable reference names,
`audita_llm_proposal_api_key_env` and `audita_llm_validation_api_key_env`; they not secret values. Every surviving reference must resolve to a nonempty value
must never contain secret values. The repository-local integration suite when Audita loads configuration, including references on unselected backends.
validates the rendered file with Audita rather than executing the CLI during An omitted or null credential permits unauthenticated local use. The role does
production convergence. not supply secret values, start a service, or probe profiles during convergence.
## Migrating from the version-1 role
This revision renders version-2 configuration only. Existing version-1 users
can retain the previous role revision while planning a coordinated migration.
The old fields have no compatibility aliases:
| Previous role inputs | Version-2 destination |
| --- | --- |
| `audita_llm_{proposal,validation}_base_url` | `audita_backends.<id>.endpoint`; both selections may share one backend. |
| `audita_llm_{proposal,validation}_api_key_env` | `audita_backends.<id>.credential`, an explicit environment-variable name. |
| `audita_concurrency_{total_llm,proposal_llm,validation_llm}` | `audita_backends.<id>.concurrency` and optional `queue_capacity`; capacity is shared by roles using that backend. |
| `audita_llm_{proposal,validation}_model` | Model in caller-owned PromptKit profile files, selected by `audita_llm_proposal_profile` and optional `audita_llm_validation_profile`. |
| `audita_llm_{proposal,validation}_timeout` | Per-attempt `timeout_seconds` in profile files; `audita_llm_operation_timeout` separately bounds a logical call. |
| `audita_llm_{proposal,validation}_max_retries` | `audita_llm_transient_retries` and `audita_llm_repair_attempts` are separate native budgets; old values do not map directly. |
| Three `audita_normalization_*` gap/duration values | Same role variables, now numeric seconds rather than duration strings. |
| `audita_diagnostics_retention` | Removed; use `audita_diagnostics_debug` and `audita_diagnostics_work_dir` for v2 debug behavior. Reporting uses `audita_reporting_enabled` and `audita_reporting_stdout_dir`. |
Omitting `audita_llm_validation_profile` lets Audita inherit the proposal
selection. `audita_llm_operation_timeout` is a Go duration string, while
normalization times are numbers. See the tagged
[v2 migration guide](https://gitea.maximumdirect.net/eric/audita/src/tag/v2.0.1/docs/migrations/v2.md)
and [configuration reference](https://gitea.maximumdirect.net/eric/audita/src/tag/v2.0.1/docs/config.md)
for exact validation and precedence rules.
## Example ## Example
Inventory can supply the required choices for a local backend:
```yaml
# host_vars/audita_host.yml (caller-owned)
audita_backends:
local:
endpoint: http://127.0.0.1:11434/v1
credential: null
audita_llm_proposal_profile: local-proposal
```
The caller also supplies `profiles/local-proposal.yaml` beside the managed
configuration, or selects another directory with `audita_profiles_dir`:
```yaml
# profiles/local-proposal.yaml (caller-owned)
id: local-proposal
backend: local
model: replace-with-an-available-local-model
timeout_seconds: 30
```
Choose a model available at the selected endpoint. These profile files must
exist before invoking Audita, but need not exist before applying the role.
The role neither deploys them nor selects a ConfigKit overlay.
```yaml ```yaml
- hosts: audita_hosts - hosts: audita_hosts
roles: roles:
@@ -91,20 +141,16 @@ production convergence.
## Validation ## Validation
The integration driver installs pinned collection dependencies, runs syntax The integration driver installs pinned collection dependencies, runs syntax
checking and ansible-lint, and validates the default configuration with Audita checking and ansible-lint, and applies the role with local fixture backends and
before requiring clean repeated convergence. It also covers one representative caller-owned profiles. It checks the temporary configuration with native
field-specific configuration with nested values, null omission, and intentional `config validate` and the effective settings and profiles views, then requires
falsey values, then validates it natively. A combined recovery scenario proves clean repeated convergence. It also tests a caller-supplied Go executable with
that a missing binary is rebuilt with a caller-supplied Go executable while dependency management disabled after removing the managed binary. The fixture
dependency management is disabled. Audita currently has no native configuration and sibling `profiles/` directory live under the script's private
release-identity command, so the suite intentionally does not use a source temporary root, which cleanup removes. The role still mutates the local managed
commit as a version-test proxy. source checkout and binary; an existing system configuration is not replaced.
On a fresh host, default convergence may also install Git through APT, refresh These offline checks do not establish provider connectivity, runtime report
the APT cache, and install the Go Snap. Application state is limited to behavior, or compatibility with the unchanged D&D suite:
`/usr/local/src/audita`, `/usr/local/bin/audita`, and
`/usr/local/etc/audita/config.yml`; temporary test-specific files remain inside
the suite's private temporary directory. The suite restores the default
deployment during cleanup:
```sh ```sh
tests/integration tests/integration

View File

@@ -1,5 +1,5 @@
--- ---
audita_version: v0.9.1 audita_version: v2.0.1
audita_repository_url: https://gitea.maximumdirect.net/eric/audita.git audita_repository_url: https://gitea.maximumdirect.net/eric/audita.git
audita_source_dir: /usr/local/src/audita audita_source_dir: /usr/local/src/audita
audita_binary_path: /usr/local/bin/audita audita_binary_path: /usr/local/bin/audita
@@ -11,22 +11,15 @@ audita_config_owner: root
audita_config_group: root audita_config_group: root
audita_config_directory_mode: "0755" audita_config_directory_mode: "0755"
audita_config_file_mode: "0644" audita_config_file_mode: "0644"
audita_config_version: 1 audita_config_version: 2
audita_profiles_dir: null
audita_pipeline_modules: null audita_pipeline_modules: null
audita_output_schema: null audita_output_schema: null
audita_llm_proposal_base_url: null audita_llm_validation_profile: null
audita_llm_proposal_model: null audita_llm_transient_retries: null
audita_llm_proposal_api_key_env: null audita_llm_repair_attempts: null
audita_llm_proposal_timeout: null audita_llm_operation_timeout: null
audita_llm_proposal_max_retries: null audita_validation_max_prompt_tokens: null
audita_llm_validation_base_url: null
audita_llm_validation_model: null
audita_llm_validation_api_key_env: null
audita_llm_validation_timeout: null
audita_llm_validation_max_retries: null
audita_concurrency_total_llm: null
audita_concurrency_proposal_llm: null
audita_concurrency_validation_llm: null
audita_chunking_target_sections: null audita_chunking_target_sections: null
audita_chunking_max_section_tokens: null audita_chunking_max_section_tokens: null
audita_chunking_min_section_tokens: null audita_chunking_min_section_tokens: null
@@ -39,5 +32,7 @@ audita_thresholds_homophones: null
audita_thresholds_spoken_word: null audita_thresholds_spoken_word: null
audita_thresholds_grammar: null audita_thresholds_grammar: null
audita_context_description: null audita_context_description: null
audita_reporting_enabled: null
audita_reporting_stdout_dir: null
audita_diagnostics_debug: null
audita_diagnostics_work_dir: null audita_diagnostics_work_dir: null
audita_diagnostics_retention: null

View File

@@ -55,6 +55,14 @@ argument_specs:
type: int type: int
required: true required: true
description: Version of the Audita configuration schema. description: Version of the Audita configuration schema.
audita_profiles_dir:
type: path
required: false
description: Optional caller-managed PromptKit profile directory.
audita_backends:
type: dict
required: true
description: Native backend mapping supplied by the caller.
audita_pipeline_modules: audita_pipeline_modules:
type: list type: list
elements: str elements: str
@@ -64,58 +72,30 @@ argument_specs:
type: str type: str
required: false required: false
description: Optional output schema. description: Optional output schema.
audita_llm_proposal_base_url: audita_llm_proposal_profile:
type: str
required: true
description: Caller-selected proposal PromptKit profile ID.
audita_llm_validation_profile:
type: str type: str
required: false required: false
description: Optional proposal LLM base URL. description: Optional validation PromptKit profile ID; omission inherits proposal.
audita_llm_proposal_model: audita_llm_transient_retries:
type: str
required: false
description: Optional proposal LLM model.
audita_llm_proposal_api_key_env:
type: str
required: false
description: Optional proposal LLM API key environment variable name.
audita_llm_proposal_timeout:
type: raw
required: false
description: Optional proposal LLM timeout in seconds or duration form.
audita_llm_proposal_max_retries:
type: int type: int
required: false required: false
description: Optional proposal LLM retry count. description: Optional additional transient attempts per generation.
audita_llm_validation_base_url: audita_llm_repair_attempts:
type: int
required: false
description: Optional additional structural repair generations.
audita_llm_operation_timeout:
type: str type: str
required: false required: false
description: Optional validation LLM base URL. description: Optional logical-operation deadline as a Go duration.
audita_llm_validation_model: audita_validation_max_prompt_tokens:
type: str
required: false
description: Optional validation LLM model.
audita_llm_validation_api_key_env:
type: str
required: false
description: Optional validation LLM API key environment variable name.
audita_llm_validation_timeout:
type: raw
required: false
description: Optional validation LLM timeout in seconds or duration form.
audita_llm_validation_max_retries:
type: int type: int
required: false required: false
description: Optional validation LLM retry count. description: Optional validation batching token budget.
audita_concurrency_total_llm:
type: int
required: false
description: Optional total LLM concurrency.
audita_concurrency_proposal_llm:
type: int
required: false
description: Optional proposal LLM concurrency.
audita_concurrency_validation_llm:
type: int
required: false
description: Optional validation LLM concurrency.
audita_chunking_target_sections: audita_chunking_target_sections:
type: int type: int
required: false required: false
@@ -129,17 +109,17 @@ argument_specs:
required: false required: false
description: Optional minimum section token count. description: Optional minimum section token count.
audita_normalization_max_segment_gap: audita_normalization_max_segment_gap:
type: raw type: float
required: false required: false
description: Optional maximum segment gap in seconds or duration form. description: Optional maximum segment gap in numeric seconds.
audita_normalization_ellipsis_gap: audita_normalization_ellipsis_gap:
type: raw type: float
required: false required: false
description: Optional ellipsis gap in seconds or duration form. description: Optional ellipsis gap in numeric seconds.
audita_normalization_max_segment_duration: audita_normalization_max_segment_duration:
type: raw type: float
required: false required: false
description: Optional maximum segment duration in seconds or duration form. description: Optional maximum segment duration in numeric seconds.
audita_normalization_max_segment_tokens: audita_normalization_max_segment_tokens:
type: int type: int
required: false required: false
@@ -164,11 +144,19 @@ argument_specs:
type: str type: str
required: false required: false
description: Optional transcript context description. description: Optional transcript context description.
audita_reporting_enabled:
type: bool
required: false
description: Optional ordinary report-bundle policy.
audita_reporting_stdout_dir:
type: str
required: false
description: Optional report directory for stdout output.
audita_diagnostics_debug:
type: bool
required: false
description: Optional detailed debug-artifact policy.
audita_diagnostics_work_dir: audita_diagnostics_work_dir:
type: str type: str
required: false required: false
description: Optional diagnostics work directory. description: Optional diagnostics work directory.
audita_diagnostics_retention:
type: str
required: false
description: Optional diagnostics retention policy.

View File

@@ -1,4 +1,22 @@
version: {{ audita_config_version }} version: {{ audita_config_version | to_json }}
{% if audita_profiles_dir is not none %}
profiles_dir: {{ audita_profiles_dir | to_json }}
{% endif %}
backends: {{ audita_backends | to_json }}
llm:
proposal_profile: {{ audita_llm_proposal_profile | to_json }}
{% if audita_llm_validation_profile is not none %}
validation_profile: {{ audita_llm_validation_profile | to_json }}
{% endif %}
{% if audita_llm_transient_retries is not none %}
transient_retries: {{ audita_llm_transient_retries | to_json }}
{% endif %}
{% if audita_llm_repair_attempts is not none %}
repair_attempts: {{ audita_llm_repair_attempts | to_json }}
{% endif %}
{% if audita_llm_operation_timeout is not none %}
operation_timeout: {{ audita_llm_operation_timeout | to_json }}
{% endif %}
{% if audita_pipeline_modules is not none %} {% if audita_pipeline_modules is not none %}
pipeline: pipeline:
modules: {{ audita_pipeline_modules | to_json }} modules: {{ audita_pipeline_modules | to_json }}
@@ -7,56 +25,9 @@ pipeline:
output: output:
schema: {{ audita_output_schema | to_json }} schema: {{ audita_output_schema | to_json }}
{% endif %} {% endif %}
{% if audita_llm_proposal_base_url is not none or audita_llm_proposal_model is not none or audita_llm_proposal_api_key_env is not none or audita_llm_proposal_timeout is not none or audita_llm_proposal_max_retries is not none or audita_llm_validation_base_url is not none or audita_llm_validation_model is not none or audita_llm_validation_api_key_env is not none or audita_llm_validation_timeout is not none or audita_llm_validation_max_retries is not none %} {% if audita_validation_max_prompt_tokens is not none %}
llm: validation:
{% if audita_llm_proposal_base_url is not none or audita_llm_proposal_model is not none or audita_llm_proposal_api_key_env is not none or audita_llm_proposal_timeout is not none or audita_llm_proposal_max_retries is not none %} max_prompt_tokens: {{ audita_validation_max_prompt_tokens | to_json }}
proposal:
{% if audita_llm_proposal_base_url is not none %}
base_url: {{ audita_llm_proposal_base_url | to_json }}
{% endif %}
{% if audita_llm_proposal_model is not none %}
model: {{ audita_llm_proposal_model | to_json }}
{% endif %}
{% if audita_llm_proposal_api_key_env is not none %}
api_key_env: {{ audita_llm_proposal_api_key_env | to_json }}
{% endif %}
{% if audita_llm_proposal_timeout is not none %}
timeout: {{ audita_llm_proposal_timeout | to_json }}
{% endif %}
{% if audita_llm_proposal_max_retries is not none %}
max_retries: {{ audita_llm_proposal_max_retries | to_json }}
{% endif %}
{% endif %}
{% if audita_llm_validation_base_url is not none or audita_llm_validation_model is not none or audita_llm_validation_api_key_env is not none or audita_llm_validation_timeout is not none or audita_llm_validation_max_retries is not none %}
validation:
{% if audita_llm_validation_base_url is not none %}
base_url: {{ audita_llm_validation_base_url | to_json }}
{% endif %}
{% if audita_llm_validation_model is not none %}
model: {{ audita_llm_validation_model | to_json }}
{% endif %}
{% if audita_llm_validation_api_key_env is not none %}
api_key_env: {{ audita_llm_validation_api_key_env | to_json }}
{% endif %}
{% if audita_llm_validation_timeout is not none %}
timeout: {{ audita_llm_validation_timeout | to_json }}
{% endif %}
{% if audita_llm_validation_max_retries is not none %}
max_retries: {{ audita_llm_validation_max_retries | to_json }}
{% endif %}
{% endif %}
{% endif %}
{% if audita_concurrency_total_llm is not none or audita_concurrency_proposal_llm is not none or audita_concurrency_validation_llm is not none %}
concurrency:
{% if audita_concurrency_total_llm is not none %}
total_llm: {{ audita_concurrency_total_llm | to_json }}
{% endif %}
{% if audita_concurrency_proposal_llm is not none %}
proposal_llm: {{ audita_concurrency_proposal_llm | to_json }}
{% endif %}
{% if audita_concurrency_validation_llm is not none %}
validation_llm: {{ audita_concurrency_validation_llm | to_json }}
{% endif %}
{% endif %} {% endif %}
{% if audita_chunking_target_sections is not none or audita_chunking_max_section_tokens is not none or audita_chunking_min_section_tokens is not none %} {% if audita_chunking_target_sections is not none or audita_chunking_max_section_tokens is not none or audita_chunking_min_section_tokens is not none %}
chunking: chunking:
@@ -73,13 +44,13 @@ chunking:
{% if audita_normalization_max_segment_gap is not none or audita_normalization_ellipsis_gap is not none or audita_normalization_max_segment_duration is not none or audita_normalization_max_segment_tokens is not none %} {% if audita_normalization_max_segment_gap is not none or audita_normalization_ellipsis_gap is not none or audita_normalization_max_segment_duration is not none or audita_normalization_max_segment_tokens is not none %}
normalization: normalization:
{% if audita_normalization_max_segment_gap is not none %} {% if audita_normalization_max_segment_gap is not none %}
max_segment_gap: {{ audita_normalization_max_segment_gap | to_json }} max_segment_gap: {{ audita_normalization_max_segment_gap | float | to_json }}
{% endif %} {% endif %}
{% if audita_normalization_ellipsis_gap is not none %} {% if audita_normalization_ellipsis_gap is not none %}
ellipsis_gap: {{ audita_normalization_ellipsis_gap | to_json }} ellipsis_gap: {{ audita_normalization_ellipsis_gap | float | to_json }}
{% endif %} {% endif %}
{% if audita_normalization_max_segment_duration is not none %} {% if audita_normalization_max_segment_duration is not none %}
max_segment_duration: {{ audita_normalization_max_segment_duration | to_json }} max_segment_duration: {{ audita_normalization_max_segment_duration | float | to_json }}
{% endif %} {% endif %}
{% if audita_normalization_max_segment_tokens is not none %} {% if audita_normalization_max_segment_tokens is not none %}
max_segment_tokens: {{ audita_normalization_max_segment_tokens | to_json }} max_segment_tokens: {{ audita_normalization_max_segment_tokens | to_json }}
@@ -104,12 +75,21 @@ thresholds:
context: context:
description: {{ audita_context_description | to_json }} description: {{ audita_context_description | to_json }}
{% endif %} {% endif %}
{% if audita_diagnostics_work_dir is not none or audita_diagnostics_retention is not none %} {% if audita_reporting_enabled is not none or audita_reporting_stdout_dir is not none %}
reporting:
{% if audita_reporting_enabled is not none %}
enabled: {{ audita_reporting_enabled | to_json }}
{% endif %}
{% if audita_reporting_stdout_dir is not none %}
stdout_dir: {{ audita_reporting_stdout_dir | to_json }}
{% endif %}
{% endif %}
{% if audita_diagnostics_debug is not none or audita_diagnostics_work_dir is not none %}
diagnostics: diagnostics:
{% if audita_diagnostics_debug is not none %}
debug: {{ audita_diagnostics_debug | to_json }}
{% endif %}
{% if audita_diagnostics_work_dir is not none %} {% if audita_diagnostics_work_dir is not none %}
work_dir: {{ audita_diagnostics_work_dir | to_json }} work_dir: {{ audita_diagnostics_work_dir | to_json }}
{% endif %} {% endif %}
{% if audita_diagnostics_retention is not none %}
retention: {{ audita_diagnostics_retention | to_json }}
{% endif %}
{% endif %} {% endif %}

View File

@@ -14,12 +14,13 @@ converge="$repository_root/tests/converge.yml"
verify_config="$repository_root/tests/verify-config.yml" verify_config="$repository_root/tests/verify-config.yml"
ansible_playbook="$workspace_venv/bin/ansible-playbook" ansible_playbook="$workspace_venv/bin/ansible-playbook"
binary_path=/usr/local/bin/audita binary_path=/usr/local/bin/audita
config_path=/usr/local/etc/audita/config.yml config_root="$test_tmp/config"
config_path="$config_root/config.yml"
profile_dir="$config_root/profiles"
cleanup() { cleanup() {
exit_status=$? exit_status=$?
trap - EXIT trap - EXIT
"$ansible_playbook" -i "$inventory" "$converge" >/dev/null 2>&1 || true
rm -rf -- "$test_tmp" rm -rf -- "$test_tmp"
exit "$exit_status" exit "$exit_status"
} }
@@ -32,31 +33,111 @@ require_clean_recap() {
run_converge() { run_converge() {
output_file=$1 output_file=$1
shift shift
"$ansible_playbook" -i "$inventory" "$@" "$converge" | tee "$output_file" "$ansible_playbook" -i "$inventory" "${baseline_args[@]}" "$@" "$converge" | tee "$output_file"
} }
validate_config() { validate_config() {
"$binary_path" config validate --config "$config_path" "$binary_path" config validate --config "$config_path"
} }
verify_profiles() {
expected_validation_id=$1
expected_validation_model=$2
expected_validation_timeout=$3
"$binary_path" config print-effective --config "$config_path" --view profiles | \
"$workspace_venv/bin/python" -c '
import sys
import yaml
profiles = yaml.safe_load(sys.stdin)
assert profiles["proposal"]["id"] == "fixture-proposal"
assert profiles["proposal"]["backend"] == "fixture"
assert profiles["proposal"]["model"] == "offline-fixture"
assert profiles["proposal"]["timeout_seconds"] == 30
assert profiles["validation"]["id"] == sys.argv[1]
assert profiles["validation"]["backend"] == "fixture"
assert profiles["validation"]["model"] == sys.argv[2]
assert profiles["validation"]["timeout_seconds"] == int(sys.argv[3])
' "$expected_validation_id" "$expected_validation_model" "$expected_validation_timeout"
}
mkdir -p "$profile_dir"
printf '%s\n' \
'id: fixture-proposal' \
'backend: fixture' \
'model: offline-fixture' \
'timeout_seconds: 30' > "$profile_dir/fixture-proposal.yaml"
printf '%s\n' \
'id: fixture-validation' \
'backend: fixture' \
'model: offline-validation' \
'timeout_seconds: 45' > "$profile_dir/fixture-validation.yaml"
baseline_args=(
-e "audita_config_path=$config_path"
-e "audita_config_owner=$(id -un)"
-e "audita_config_group=$(id -gn)"
-e '{"audita_backends": {"fixture": {"endpoint": "http://127.0.0.1:11434/v1", "credential": null}}, "audita_llm_proposal_profile": "fixture-proposal"}'
)
cd "$repository_root" cd "$repository_root"
"$workspace_venv/bin/ansible-galaxy" collection install \ "$workspace_venv/bin/ansible-galaxy" collection install \
-r collections/requirements.yml \ -r collections/requirements.yml \
-p "$ANSIBLE_COLLECTIONS_PATH" -p "$ANSIBLE_COLLECTIONS_PATH"
"$ansible_playbook" -i "$inventory" --syntax-check "$converge" "$ansible_playbook" -i "$inventory" "${baseline_args[@]}" --syntax-check "$converge"
"$workspace_venv/bin/ansible-lint" "$converge" "$verify_config" "$workspace_venv/bin/ansible-lint" "$converge" "$verify_config"
baseline_log="$test_tmp/baseline.log" baseline_log="$test_tmp/baseline.log"
run_converge "$baseline_log" run_converge "$baseline_log"
validate_config validate_config
verify_profiles fixture-proposal offline-fixture 30
"$ansible_playbook" -i "$inventory" \
-e "audita_test_config_path=$config_path" \
-e 'audita_test_case=baseline' "$verify_config"
baseline_repeat_log="$test_tmp/baseline-repeat.log" baseline_repeat_log="$test_tmp/baseline-repeat.log"
run_converge "$baseline_repeat_log" run_converge "$baseline_repeat_log"
require_clean_recap "$baseline_repeat_log" require_clean_recap "$baseline_repeat_log"
representative_config='{"audita_config_version": 1, "audita_pipeline_modules": ["glossary"], "audita_llm_proposal_base_url": "https://openrouter.ai/api/v1", "audita_llm_proposal_model": "openrouter/google/gemma-4-31b-it", "audita_llm_proposal_timeout": "10s", "audita_llm_proposal_max_retries": 0, "audita_context_description": "", "audita_diagnostics_retention": "never", "audita_llm_validation_model": null, "audita_output_schema": null, "audita_diagnostics_work_dir": null}' representative_config='{
"audita_backend_base": {"fixture": {"endpoint": "http://127.0.0.1:11434/v1", "credential": null}},
"audita_backend_capacity": {"fixture": {"concurrency": 2, "queue_capacity": 0}},
"audita_backends": "{{ audita_backend_base | combine(audita_backend_capacity, recursive=true) }}",
"audita_profiles_dir": "profiles",
"audita_llm_validation_profile": "fixture-validation",
"audita_llm_transient_retries": 0,
"audita_llm_repair_attempts": 0,
"audita_llm_operation_timeout": "45s",
"audita_validation_max_prompt_tokens": 128,
"audita_pipeline_modules": [],
"audita_normalization_max_segment_gap": 1.25,
"audita_normalization_ellipsis_gap": 2.5,
"audita_normalization_max_segment_duration": 30.75,
"audita_context_description": "",
"audita_reporting_enabled": false,
"audita_reporting_stdout_dir": "",
"audita_diagnostics_debug": false
}'
representative_log="$test_tmp/representative-config.log" representative_log="$test_tmp/representative-config.log"
run_converge "$representative_log" -e "$representative_config" run_converge "$representative_log" -e "$representative_config"
"$ansible_playbook" -i "$inventory" "$verify_config" validate_config
verify_profiles fixture-validation offline-validation 45
"$binary_path" config print-effective --config "$config_path" --view settings | \
"$workspace_venv/bin/python" -c '
import sys
import yaml
settings = yaml.safe_load(sys.stdin)
assert settings["backends"]["fixture"]["concurrency"] == 2
assert settings["backends"]["fixture"]["queue_capacity"] == 0
assert settings["llm"]["validation_profile"] == "fixture-validation"
assert settings["llm"]["transient_retries"] == 0
assert settings["llm"]["repair_attempts"] == 0
assert settings["llm"]["operation_timeout"] == "45s"
assert settings["normalization"]["max_segment_gap"] == 1.25
assert settings["reporting"]["enabled"] is False
assert settings["diagnostics"]["debug"] is False
'
"$ansible_playbook" -i "$inventory" \
-e "audita_test_config_path=$config_path" \
-e 'audita_test_case=representative' "$verify_config"
representative_repeat_log="$test_tmp/representative-config-repeat.log" representative_repeat_log="$test_tmp/representative-config-repeat.log"
run_converge "$representative_repeat_log" -e "$representative_config" run_converge "$representative_repeat_log" -e "$representative_config"
require_clean_recap "$representative_repeat_log" require_clean_recap "$representative_repeat_log"

View File

@@ -2,36 +2,52 @@
- name: Verify rendered Audita configuration - name: Verify rendered Audita configuration
hosts: localhost hosts: localhost
gather_facts: false gather_facts: false
vars:
audita_rendered_config: "{{ audita_rendered_config_file.content | b64decode | from_yaml }}"
tasks: tasks:
- name: Read rendered configuration - name: Read rendered configuration
become: true
ansible.builtin.slurp: ansible.builtin.slurp:
src: /usr/local/etc/audita/config.yml src: "{{ audita_test_config_path }}"
register: audita_rendered_config_file register: audita_rendered_config_file
- name: Check omitted baseline settings
ansible.builtin.assert:
that:
- audita_rendered_config.profiles_dir is not defined
- audita_rendered_config.llm.validation_profile is not defined
- audita_rendered_config.llm.transient_retries is not defined
- audita_rendered_config.llm.repair_attempts is not defined
- audita_rendered_config.llm.operation_timeout is not defined
- audita_rendered_config.reporting is not defined
- audita_rendered_config.diagnostics is not defined
when: audita_test_case == "baseline"
- name: Check representative rendered configuration - name: Check representative rendered configuration
ansible.builtin.assert: ansible.builtin.assert:
that: that:
- audita_rendered_config.version == 1 - audita_rendered_config.version == 2
- audita_rendered_config.pipeline.modules == ["glossary"] - audita_rendered_config.profiles_dir == "profiles"
- audita_rendered_config.llm.proposal.base_url == "https://openrouter.ai/api/v1" - audita_rendered_config.backends.fixture.endpoint == "http://127.0.0.1:11434/v1"
- audita_rendered_config.llm.proposal.model == "openrouter/google/gemma-4-31b-it" - audita_rendered_config.backends.fixture.credential is none
- audita_rendered_config.llm.proposal.timeout == "10s" - audita_rendered_config.backends.fixture.concurrency == 2
- audita_rendered_config.llm.proposal.max_retries == 0 - audita_rendered_config.backends.fixture.queue_capacity == 0
- audita_rendered_config.llm.proposal_profile == "fixture-proposal"
- audita_rendered_config.llm.validation_profile == "fixture-validation"
- audita_rendered_config.llm.transient_retries == 0
- audita_rendered_config.llm.repair_attempts == 0
- audita_rendered_config.llm.operation_timeout == "45s"
- audita_rendered_config.validation.max_prompt_tokens == 128
- audita_rendered_config.pipeline.modules == []
- audita_rendered_config.normalization.max_segment_gap == 1.25
- audita_rendered_config.normalization.max_segment_gap is number
- audita_rendered_config.normalization.ellipsis_gap == 2.5
- audita_rendered_config.normalization.ellipsis_gap is number
- audita_rendered_config.normalization.max_segment_duration == 30.75
- audita_rendered_config.normalization.max_segment_duration is number
- audita_rendered_config.context.description == "" - audita_rendered_config.context.description == ""
- audita_rendered_config.diagnostics.retention == "never" - audita_rendered_config.reporting.enabled == false
- audita_rendered_config.llm.validation is not defined - audita_rendered_config.reporting.stdout_dir == ""
- audita_rendered_config.diagnostics.debug == false
- audita_rendered_config.output is not defined - audita_rendered_config.output is not defined
- audita_rendered_config.diagnostics.work_dir is not defined - audita_rendered_config.diagnostics.work_dir is not defined
vars: when: audita_test_case == "representative"
audita_rendered_config: "{{ audita_rendered_config_file.content | b64decode | from_yaml }}"
- name: Validate rendered configuration
ansible.builtin.command:
argv:
- /usr/local/bin/audita
- config
- validate
- --config
- /usr/local/etc/audita/config.yml
changed_when: false