lifeloop-cli 0.2.0

Provider-neutral lifecycle abstraction and normalizer for AI harnesses
Documentation
.PHONY: fmt fmt-check clippy test build verify wire-check lockfile-check \
	mutants-all \
	mutants-rfc0001-list mutants-rfc0001 \
	mutants-wire-list mutants-wire \
	mutants-router-list mutants-router \
	mutants-receipts-list mutants-receipts \
	mutants-subprocess-list mutants-subprocess \
	mutants-host-assets-list mutants-host-assets \
	commit bump-schema

MUTANTS_RFC0001_RE := PayloadEnvelope::effective_byte_size|PayloadReceipt::validate|decide_placement|payload_receipts_from
MUTANTS_WIRE_RE := lifecycle_event_kinds|default_retry|require_non_empty|validate|into_receipt|required|CallbackResponse::ok|CallbackResponse::failed|DispatchEnvelope::new
MUTANTS_ROUTER_RE := route|require_non_empty|validate_frame_context|require_frame_context_for_event|classify_capability|support_satisfies|strongest|manifest_support_for|manifest_placement_for|evaluate_one|decide_placement|failure_class_for|classes_for_negotiation_outcome|retry_class_for|validate_receipt_eligible
MUTANTS_RECEIPTS_RE := SequenceGenerator|InMemoryIdempotencyStore|synthesize_and_emit|derive_status|payload_receipts_from|merged_warnings|LifecycleReceipt::validate|into_receipt|required
MUTANTS_SUBPROCESS_RE := failure_class_for_subprocess_error|transport_error_for|invoke_inner|read_to_end_limited|read_to_end_truncated|remaining|wait_with_deadline
MUTANTS_HOST_ASSETS_RE := render_|merge_|status|supports_mode|supported_modes|combine_actions
MUTANTS_ALL_SLICES := rfc0001 wire router receipts subprocess host-assets
MUTANTS_OUTPUT_ROOT ?= mutants.out/all
# Accepted cargo-mutants survivors are descriptor-based rather than line-based
# so normal code movement does not silently change the allowlist.
MUTANTS_SUBPROCESS_EQUIVALENT_RE := replace < with == in wait_with_deadline|replace < with > in wait_with_deadline|replace < with <= in wait_with_deadline|replace \* with / in wait_with_deadline
MUTANTS_HOST_ASSETS_EQUIVALENT_RE := replace match guard raw\.trim\(\)\.is_empty\(\) with false in merge_codex_config_text|delete match arm \(HostAdapter::Codex, IntegrationMode::ManualSkill\) in render_applied_assets_with_profile

fmt:
	bash scripts/fmt.sh

fmt-check:
	bash scripts/fmt.sh --check

clippy:
	cargo clippy --workspace --all-targets --all-features -- -D warnings

test:
	cargo test --workspace --all-targets --all-features

build:
	cargo build --workspace --all-targets --all-features

wire-check:
	bash scripts/check-wire-contract.sh

lockfile-check:
	bash scripts/check-lockfile.sh

mutants-all:
	@mkdir -p "$(MUTANTS_OUTPUT_ROOT)"
	@status=0; \
	for slice in $(MUTANTS_ALL_SLICES); do \
		output="$(MUTANTS_OUTPUT_ROOT)/$$slice"; \
		printf '\n==> mutants-%s (output: %s)\n' "$$slice" "$$output"; \
		if CARGO_MUTANTS_OUTPUT="$$output" $(MAKE) --no-print-directory "mutants-$$slice"; then \
			printf '==> mutants-%s passed\n' "$$slice"; \
		else \
			rc=$$?; \
			status=1; \
			printf '==> mutants-%s failed with exit %s (output: %s)\n' "$$slice" "$$rc" "$$output" >&2; \
		fi; \
	done; \
	exit $$status

mutants-rfc0001-list:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/wire.rs \
		--file src/router/negotiation.rs \
		--file src/router/receipts.rs \
		--re '$(MUTANTS_RFC0001_RE)' \
		--list

mutants-rfc0001:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/wire.rs \
		--file src/router/negotiation.rs \
		--file src/router/receipts.rs \
		--re '$(MUTANTS_RFC0001_RE)' \
		--baseline run \
		--timeout 1200 \
		-- \
		--test wire_contract \
		--test spec_alignment \
		--test router_negotiation \
		--test router_receipts \
		--test cli_event \
		--test fixity_pilot_event_invoke \
		-- \
		--skip event_invoke_happy_path_emits_lifecycle_receipt

mutants-wire-list:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/wire.rs \
		--file src/receipt_contract.rs \
		--file src/callback_contract.rs \
		--file src/manifest_contract.rs \
		--re '$(MUTANTS_WIRE_RE)' \
		--list

mutants-wire:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/wire.rs \
		--file src/receipt_contract.rs \
		--file src/callback_contract.rs \
		--file src/manifest_contract.rs \
		--re '$(MUTANTS_WIRE_RE)' \
		--baseline run \
		--timeout 1200 \
		-- \
		--test wire_contract \
		--test spec_alignment \
		--test conformance

mutants-router-list:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/router/plan.rs \
		--file src/router/validation.rs \
		--file src/router/negotiation.rs \
		--file src/router/failure_mapping.rs \
		--re '$(MUTANTS_ROUTER_RE)' \
		--list

mutants-router:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/router/plan.rs \
		--file src/router/validation.rs \
		--file src/router/negotiation.rs \
		--file src/router/failure_mapping.rs \
		--re '$(MUTANTS_ROUTER_RE)' \
		--baseline run \
		--timeout 1200 \
		-- \
		--test router \
		--test router_negotiation \
		--test router_failure_mapping

mutants-receipts-list:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/router/receipts.rs \
		--file src/receipt_contract.rs \
		--re '$(MUTANTS_RECEIPTS_RE)' \
		--list

mutants-receipts:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/router/receipts.rs \
		--file src/receipt_contract.rs \
		--re '$(MUTANTS_RECEIPTS_RE)' \
		--baseline run \
		--timeout 1200 \
		-- \
		--test router_receipts \
		--test cli_receipt \
		--test cli_event \
		--test wire_contract \
		-- \
		--skip event_invoke_happy_path_emits_lifecycle_receipt

mutants-subprocess-list:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/router/subprocess.rs \
		--file src/router/callbacks.rs \
		--re '$(MUTANTS_SUBPROCESS_RE)' \
		--exclude-re '$(MUTANTS_SUBPROCESS_EQUIVALENT_RE)' \
		--list

mutants-subprocess:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/router/subprocess.rs \
		--file src/router/callbacks.rs \
		--re '$(MUTANTS_SUBPROCESS_RE)' \
		--exclude-re '$(MUTANTS_SUBPROCESS_EQUIVALENT_RE)' \
		--baseline run \
		--timeout 1200 \
		-- \
		--lib \
		--test router_e2e_subprocess \
		--test router_callbacks

mutants-host-assets-list:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/host_assets.rs \
		--file 'src/host_assets/*.rs' \
		--re '$(MUTANTS_HOST_ASSETS_RE)' \
		--exclude-re '$(MUTANTS_HOST_ASSETS_EQUIVALENT_RE)' \
		--list

mutants-host-assets:
	cargo mutants -p lifeloop-cli --all-features \
		--file src/host_assets.rs \
		--file 'src/host_assets/*.rs' \
		--re '$(MUTANTS_HOST_ASSETS_RE)' \
		--exclude-re '$(MUTANTS_HOST_ASSETS_EQUIVALENT_RE)' \
		--baseline run \
		--timeout 1200 \
		-- \
		--test host_assets \
		--test host_assets_profiles \
		--test cli_asset

verify: fmt-check clippy test build

commit:
	@if [ -z "$(MSG)" ] || [ -z "$(PATHS)" ]; then \
		echo 'usage: make commit MSG="<message>" PATHS="<path> [<path>...]"' >&2; exit 2; \
	fi
	@# `set -f` keeps PATHS from being shell-globbed before printf sees
	@# it; printf delivers each path on its own line over stdin, where
	@# scripts/commit.sh enforces the bulk-add guard at the same layer
	@# that performs the commit (see commit.sh for full rationale).
	set -f && printf '%s\n' $(PATHS) | bash scripts/commit.sh -m "$(MSG)"

bump-schema:
	@if [ -z "$(VERSION)" ] || [ -z "$(REASON)" ]; then \
		echo 'usage: make bump-schema VERSION=<new> REASON="<why>"' >&2; exit 2; \
	fi
	cargo run --quiet --bin lifeloop-bump-schema -- "$(VERSION)" "$(REASON)"