# AIM add-on development contract **Core 3.3.0rc8; service/wire/event 1.0 (stable additive 1.x); Ansible Core 2.19.11.** AIM is the independently usable Source of Truth. Add-ons own presentation, sessions, authorization at their application boundary, approvals, queues and persistence. They never patch Core, scrape terminal output or duplicate inventory/execution rules. ## Integration sequence Use `aim.services.v1` or one `aimctl request` process per JSON-lines request. Discover capabilities, current customers/hosts/hierarchy and catalog. Prepare explicit hosts and declared options, show the normalized request and result contract for review, check readiness under the actual worker identity, acquire a worker slot, collect fresh credentials, and execute using the prepared revision. A stale revision requires review. Omit unchanged options to preserve inventory/role-default precedence. The published API is in `scripts/docs/ADDON_API.md`; the complete new result contract is in `scripts/docs/OPERATION_RESULTS.md`. Current support and qualification are in `ADDON_SUPPORT.md` and `VALIDATION.md`. Never infer feature support from product version alone. `capabilities.collection_baselines` declares collection floors that add-on installers/operators must satisfy; Core does not upgrade them. Unknown optional response fields may be ignored; unknown errors fail safely. ## Purposeful output: new in this candidate Check `capabilities.operation_results`. Catalog `result` is null or a declaration with schema, scope, required flag, limits and resolved `data_schema`. No new execute request field is needed. Reports are available in summary and detail mode through the final `RunResult.operation_result` and the existing final result event. There is no new raw stdout event. Parse final JSON without scraping PLAY/TASK text or debug bodies. A result contains `hosts[host]` with `status`, `schema`, `data`, `error`, plus optional `global`. Render data only when status is `available`. Missing/withheld/invalid/null is not an empty valid report. `complete` means all declared report slots arrived and passed validation; it does not mean the operation succeeded. Partial native runs can contain useful data from successful or failed targets. Preserve overall status/stage/exit, `remote_work_may_have_started`, target summary and report availability as separate facts. Never automatically retry a `result_validation` failure: remote changes may be complete. Use a generic bounded JSON/table renderer for unknown schemas, with optional richer views for known schemas. Escape text/HTML/Rich markup. Do not treat null versions as zero, pending updates as installed, excluded services as failed starts, or redacted configuration values as absent settings. Config reports expose sections but redact recognized secret/command fields; restrict report visibility and retention accordingly. ## Safety and identity Core checks active OS group membership and filesystem access. This is same-UID local execution, not per-browser-user RBAC or an untrusted-tenant sandbox. The execution account owns its 0600 keys and private staging. `service_user` is not a local UID switch. `runtime.private_key_owner` affects new keys only. Do not relax permissions, export keys, add generic sudo wrappers or run the web frontend as root to bypass the boundary. Hardened service installers must provision both applicable staging locations (process HOME and passwd home) and narrow ReadWritePaths exceptions. Retain ProtectHome/read-only, ProtectSystem/strict and private tmp protections. Run `aimctl staging-check` inside the actual sandbox. Core does not create accounts, modify units or restart add-on services. Use OneRunCredentials or a private inherited FD, never passwords in request JSON, argv, environment, URLs, logs, queue records or databases. Queue plans, not secrets. Fresh credentials are required for each attempt. Password inputs are native defaults, not Custom overrides or proof of password-only authentication. Cross-UID execution is not provided by Core; independently managed executors must already have authorized access. ## Progress and final outcomes `progress_mode: detail` is negotiated before preparation. Correlate play/task IDs, handle withheld names and fixed error hints, and ignore anonymous progress to avoid duplicate rows. Keep bounded queues and responsive sinks. Raw module/exception/debug output remains unsupported. Purposeful data is a separate final schema, not an expansion of host_result. Target outcomes come from Core's final native per-host stats, not task counts. An interrupted/incomplete stream must not become partial success by inference. ## Ownership and handoff Core and add-ons release independently. Core deployment preserves add-on code/state, operator aim.yml, inventories, Vaults and keys; add-ons preserve Core in return. Use ZIP + checksum + the deployer, not Git or patches. Coordinate active jobs before source replacement. Only documented retired Core files are removed on upgrade. The nine current report schemas are documented in OPERATION_RESULTS.md and advertised by the catalog. This candidate has local regression coverage, not new managed-host certification. Record independent acceptance under your actual identity/sandbox/runtime. Request new schemas/capabilities from Core instead of adding private output workarounds. ## OS patch-wave presentation (3.3.0rc8) Patch options remain catalog-driven. Interfaces should render the advertised reboot message/delay and the Windows-only `os_patching_rescan_after_reboot` option. Its catalog default is false; do not silently enable "fully patch" behavior in an add-on. Windows submits the current selected categories as one native `win_updates` wave with module-managed Windows Update sequencing and `reboot: false`. AIM evaluates reboot policy after the wave returns; it does not expose or own a per-update scheduler. With post-reboot continuation disabled, an AIM-performed reboot can still be a successful Core run while `continuation_required: true` tells the operator another patch run is needed. `remaining_updates_known: false` means the client must not invent or persist a next-wave list. If a final read-only search was performed, `remaining_updates_known: true` makes `pending` authoritative for that observation only. Render `failed_updates[].reason/message/native_code_hex` instead of parsing fatal text or Windows event logs. `install_not_allowed` deliberately does not mean "reboot required" by itself. If Core reports `blocked_reason: preexisting_reboot_required`, that is the separate preflight observation. Never auto-enable reboot, post-reboot continuation, or automatic job replay.