codexloop 0.3.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codexloop-0.3.1/.agent/rules/codexloop-architecture.md +29 -0
- codexloop-0.3.1/.agent/rules/codexloop-completion.md +42 -0
- codexloop-0.3.1/.agent/rules/codexloop-docs.md +50 -0
- codexloop-0.3.1/.agent/rules/codexloop-quota-and-capacity.md +44 -0
- codexloop-0.3.1/.agent/rules/codexloop-releasing.md +51 -0
- codexloop-0.3.1/.agent/rules/codexloop-rest-surface.md +40 -0
- codexloop-0.3.0/.cursor/rules/codexloop-run.mdc → codexloop-0.3.1/.agent/rules/codexloop-run.md +2 -5
- codexloop-0.3.1/.agent/rules/codexloop-testing.md +34 -0
- codexloop-0.3.1/.agents/skills/codexloop-architecture/SKILL.md +32 -0
- codexloop-0.3.1/.agents/skills/codexloop-completion/SKILL.md +45 -0
- codexloop-0.3.1/.agents/skills/codexloop-docs/SKILL.md +53 -0
- codexloop-0.3.1/.agents/skills/codexloop-quota-and-capacity/SKILL.md +47 -0
- codexloop-0.3.1/.agents/skills/codexloop-releasing/SKILL.md +54 -0
- codexloop-0.3.1/.agents/skills/codexloop-rest-surface/SKILL.md +43 -0
- codexloop-0.3.1/.agents/skills/codexloop-testing/SKILL.md +37 -0
- codexloop-0.3.1/.claude/skills/codexloop-architecture/SKILL.md +32 -0
- codexloop-0.3.1/.claude/skills/codexloop-completion/SKILL.md +45 -0
- codexloop-0.3.1/.claude/skills/codexloop-docs/SKILL.md +53 -0
- codexloop-0.3.1/.claude/skills/codexloop-quota-and-capacity/SKILL.md +47 -0
- codexloop-0.3.1/.claude/skills/codexloop-releasing/SKILL.md +54 -0
- codexloop-0.3.1/.claude/skills/codexloop-rest-surface/SKILL.md +43 -0
- codexloop-0.3.1/.claude/skills/codexloop-testing/SKILL.md +37 -0
- codexloop-0.3.1/.cursor/rules/codexloop-architecture.mdc +33 -0
- codexloop-0.3.1/.cursor/rules/codexloop-completion.mdc +46 -0
- codexloop-0.3.1/.cursor/rules/codexloop-docs.mdc +54 -0
- codexloop-0.3.1/.cursor/rules/codexloop-quota-and-capacity.mdc +48 -0
- codexloop-0.3.1/.cursor/rules/codexloop-releasing.mdc +55 -0
- codexloop-0.3.1/.cursor/rules/codexloop-rest-surface.mdc +44 -0
- codexloop-0.3.1/.cursor/rules/codexloop-run.mdc +14 -0
- codexloop-0.3.1/.cursor/rules/codexloop-testing.mdc +38 -0
- codexloop-0.3.1/.github/ISSUE_TEMPLATE/bug_report.yml +82 -0
- codexloop-0.3.1/.github/ISSUE_TEMPLATE/config.yml +11 -0
- codexloop-0.3.1/.github/ISSUE_TEMPLATE/feature_request.yml +38 -0
- codexloop-0.3.1/.github/PULL_REQUEST_TEMPLATE.md +12 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.gitignore +1 -0
- codexloop-0.3.1/.release-please-manifest.json +3 -0
- codexloop-0.3.1/AGENTS.md +63 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/CHANGELOG.md +13 -0
- codexloop-0.3.1/CLAUDE.md +63 -0
- codexloop-0.3.1/CODE_OF_CONDUCT.md +137 -0
- codexloop-0.3.1/CONTRIBUTING.md +170 -0
- codexloop-0.3.1/GEMINI.md +42 -0
- codexloop-0.3.1/PKG-INFO +246 -0
- codexloop-0.3.1/README.md +195 -0
- codexloop-0.3.1/SECURITY.md +88 -0
- codexloop-0.3.1/SUPPORT.md +18 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/getting-started.md +7 -2
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/index.md +1 -1
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/plans/architecture-and-roadmap.md +6 -6
- {codexloop-0.3.0 → codexloop-0.3.1}/pyproject.toml +1 -1
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/__init__.py +1 -1
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/runner.py +42 -2
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/bootstrap.py +28 -5
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/app.py +2 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/asyncio.py +2 -0
- codexloop-0.3.1/src/codexloop/cli/commands/wind_down_cmd.py +34 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/control.py +46 -1
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/gateway.py +141 -2
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/rundir.py +20 -0
- codexloop-0.3.1/tests/application/test_runner_wind_down_coverage.py +266 -0
- codexloop-0.3.1/tests/application/test_sleep_interruptible.py +131 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/application/test_waiting_runner.py +6 -3
- codexloop-0.3.1/tests/cli/test_sysexit.py +30 -0
- codexloop-0.3.1/tests/cli/test_wind_down_cmd.py +71 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_control.py +77 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_control_and_savepoints.py +15 -1
- codexloop-0.3.1/tests/infrastructure/test_event_to_dict.py +288 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_gateway.py +110 -0
- codexloop-0.3.1/tests/infrastructure/test_handoff_marker.py +137 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/live/system/test_subprocess_smoke.py +51 -0
- codexloop-0.3.0/.release-please-manifest.json +0 -3
- codexloop-0.3.0/AGENTS.md +0 -10
- codexloop-0.3.0/CLAUDE.md +0 -5
- codexloop-0.3.0/CODE_OF_CONDUCT.md +0 -4
- codexloop-0.3.0/CONTRIBUTING.md +0 -29
- codexloop-0.3.0/PKG-INFO +0 -112
- codexloop-0.3.0/README.md +0 -61
- codexloop-0.3.0/SECURITY.md +0 -13
- {codexloop-0.3.0 → codexloop-0.3.1}/.agents/skills/codexloop-run/SKILL.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.claude/skills/codexloop-run/SKILL.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.editorconfig +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.github/CODEOWNERS +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.github/workflows/ci.yml +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.github/workflows/docs.yml +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.github/workflows/publish-testpypi.yml +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.github/workflows/release-please.yml +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/.pre-commit-config.yaml +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/LICENSE +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0001-onion-import-linter.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0002-subprocess-codex-exec.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0003-credits-exhausted.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0004-adaptive-probe-loop.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0005-layered-capacity-probe.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0006-generated-openai-rest.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0007-default-approval-sandbox.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0008-policy-via-c-flags.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0009-optional-app-server.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0010-rollout-best-effort.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0011-never-consume-resets.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/0012-minimum-codex-version.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/adr/index.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/architecture/index.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/contributing.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/guides/rest-api-surface.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/plans/_shared-transplant-outline.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/plans/research-notes.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/publishing.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/reference/cli.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/docs/superpowers/plans/2026-08-13-codexloop-implementation.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/mkdocs.yml +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/release-please-config.json +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/dto.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/agent.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/api.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/control.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/doctor.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/observability.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/permissions.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/storage.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/interfaces/system.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/ports.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/usecases/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/usecases/doctor.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/usecases/list_threads.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/usecases/preflight.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/usecases/resume_thread.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/usecases/run_control.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/application/usecases/run_plan.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/approval_cmd.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/capacity.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/cwd_cmd.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/doctor.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/effort_cmd.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/logs.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/model_cmd.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/prompt.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/reset.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/resume.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/run.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/runs.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/sandbox_cmd.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/savepoints.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/snapshot.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/status.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/stop.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/threads.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/unwind.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/commands/watch.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/cli/render.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/approval.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/backoff.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/budget.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/capacity.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/classify.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/completion.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/error_codes.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/errors.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/forecast.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/handoff_marker.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/loop.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/model_profile.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/plan.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/savepoint.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/savepoint_message.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/session.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/signals.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/verbosity.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/domain/waiting.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/argv.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/events.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/probe.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/process.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/schema.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/scripted.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/agent/translate.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/api_baseline.json +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/binder.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/gateway.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/introspect.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/json_io.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/params.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/providers.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/api/registry.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/appserver/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/appserver/client.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/appserver/gateway.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/appserver/ratelimits.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/audit.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/capacity_probe.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/clock.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/config.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/control.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/doctor_env.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/events.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/git_savepoints.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/lock.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/logging.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/notify.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/progress.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/redact.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/rollout.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/snapshot.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/state.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/state_bus.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/infrastructure/stream_ui.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/src/codexloop/py.typed +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/application/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/application/fakes.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/application/test_doctor.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/application/test_interfaces_convention.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/application/test_ports.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/application/test_runner.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/application/test_usecases.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/cli/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/cli/test_api.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/cli/test_app.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/cli/test_asyncio.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/cli/test_e2e_shim.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/cli/test_ops_commands.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/cli/test_verbosity_flags.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/conftest.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/strategies.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_approval.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_backoff.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_budget.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_capacity.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_classify.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_completion.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_error_codes.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_forecast.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_handoff_marker.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_loop.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_model_profile.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_plan.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_savepoint.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_session.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_verbosity.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/domain/test_waiting.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/README.md +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/clean_completion.jsonl +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/done_with_marker.jsonl +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/huge_line.jsonl +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/malformed_line.jsonl +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/tool_heavy.jsonl +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/truncated_stream.jsonl +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/turn_failed_429_quota.jsonl +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/fixtures/jsonl/turn_failed_429_window.jsonl +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_binder.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_drift.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_gateway.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_introspect.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_json_io.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_params.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_params_extra.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_providers.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/api/test_providers_gateway_extra.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_appserver.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_appserver_gateway.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_argv.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_capacity_probe.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_config.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_coverage_boost.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_doctor_env.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_events.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_git_savepoints_units.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_lock.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_logging_redact.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_probe.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_process.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_ratelimits_extra.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_rollout.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_rollout_scan.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_rundir_run_id.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_rundir_state.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_scripted.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_snapshot.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_state_bus.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_stream_ui.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/infrastructure/test_translate.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/live/fixtures/agent_scripts/done.json +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/live/fixtures/agent_scripts/quota_then_topup.json +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/live/fixtures/agent_scripts/unknown_429.json +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/live/fixtures/agent_scripts/wait_long.json +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/live/fixtures/agent_scripts/window_then_done.json +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/live/system/conftest.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/live/system/test_matrix_inprocess.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/shim/__init__.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/shim/fake_appserver.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/shim/fake_codex.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/shim/test_fake_codex.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/test_bootstrap.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/test_no_vendor_leak.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/tests/test_packaging.py +0 -0
- {codexloop-0.3.0 → codexloop-0.3.1}/uv.lock +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# codexloop-architecture (Antigravity mirror of `.claude/skills/codexloop-architecture/SKILL.md`)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# codexloop architecture
|
|
5
|
+
|
|
6
|
+
Dependencies point inward. `import-linter` enforces this in CI.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
src/codexloop/
|
|
10
|
+
├── domain/ # PURE. stdlib only. No I/O, no async, no third-party.
|
|
11
|
+
├── application/ # Protocol ports + use cases. Imports domain + stdlib.
|
|
12
|
+
├── infrastructure/ # Adapters. ONLY layer that may import openai.
|
|
13
|
+
├── cli/ # Typer. Calls application via bootstrap.
|
|
14
|
+
└── bootstrap.py # Composition root — the ONE module that sees every layer.
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Where does new code go?
|
|
18
|
+
|
|
19
|
+
1. Touches FS, network, clock, or an SDK? → `infrastructure/`, behind a
|
|
20
|
+
`Protocol` in `application/interfaces/`. Never `import openai`
|
|
21
|
+
elsewhere.
|
|
22
|
+
2. Pure decision, zero I/O? → `domain/`. Examples: `classify.py`,
|
|
23
|
+
`waiting.py`, `completion.py`, `capacity.py`.
|
|
24
|
+
3. Orchestration (port → domain → port)? → `application/`.
|
|
25
|
+
4. Argument parsing / terminal formatting? → `cli/`.
|
|
26
|
+
|
|
27
|
+
When in doubt, push logic inward. `lint-imports` names the broken contract.
|
|
28
|
+
|
|
29
|
+
See ADR 0001.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# codexloop-completion (Antigravity mirror of `.claude/skills/codexloop-completion/SKILL.md`)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# codexloop completion detection
|
|
5
|
+
|
|
6
|
+
`CompletionEvaluator` in `domain/completion.py` maps `TurnSignals` +
|
|
7
|
+
`CapacityState` into a `CompletionVerdict`.
|
|
8
|
+
|
|
9
|
+
## CompletionVerdict variants
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
Done # task fully complete
|
|
13
|
+
Continue # remaining: list[str]
|
|
14
|
+
Blocked # reason: str — human intervention needed
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Three-layer evaluation
|
|
18
|
+
|
|
19
|
+
**Capacity rejection always outranks a completion claim.** If the turn is
|
|
20
|
+
not `Available`, return `Continue` regardless of structured output or
|
|
21
|
+
markers.
|
|
22
|
+
|
|
23
|
+
For an `Available` turn, the layers are:
|
|
24
|
+
|
|
25
|
+
1. **Structured output** (`StructuredOutput` tool call result):
|
|
26
|
+
- `blocked_on` set → `Blocked(reason=blocked_on)`.
|
|
27
|
+
- `complete: true` and `remaining_work: []` → `Done`.
|
|
28
|
+
- `complete: true` but `remaining_work` not empty → `Continue`.
|
|
29
|
+
- `complete: false` → `Continue(remaining=remaining_work)`.
|
|
30
|
+
|
|
31
|
+
2. **Marker on own line**: if `CODEXLOOP_TASK_FULLY_COMPLETE` appears on
|
|
32
|
+
its own line in the final message → `Done`.
|
|
33
|
+
|
|
34
|
+
3. **Fallback**: `Continue(remaining=[])`.
|
|
35
|
+
|
|
36
|
+
The marker check uses `_marker_on_own_line(message, done_marker)`: the
|
|
37
|
+
string must be the only non-whitespace on the line. Partial matches or
|
|
38
|
+
inline occurrences do not count.
|
|
39
|
+
|
|
40
|
+
**Never treat a non-`Available` turn as `Done`.** Rate-limit windows and
|
|
41
|
+
transient errors are waitable; completion is deferred until capacity
|
|
42
|
+
returns.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# codexloop-docs (Antigravity mirror of `.claude/skills/codexloop-docs/SKILL.md`)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# codexloop docs
|
|
5
|
+
|
|
6
|
+
User-facing documentation lives in `docs/` and is built with
|
|
7
|
+
mkdocs-material.
|
|
8
|
+
|
|
9
|
+
## Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
docs/
|
|
13
|
+
├── index.md # landing page
|
|
14
|
+
├── getting-started.md
|
|
15
|
+
├── guides/ # how-to guides
|
|
16
|
+
│ └── rest-api-surface.md
|
|
17
|
+
├── architecture/
|
|
18
|
+
│ ├── index.md
|
|
19
|
+
│ └── adr/ # Architecture Decision Records
|
|
20
|
+
│ ├── index.md
|
|
21
|
+
│ ├── 0001-onion-import-linter.md
|
|
22
|
+
│ └── 0002-subprocess-codex-exec.md
|
|
23
|
+
├── contributing.md
|
|
24
|
+
└── publishing.md
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
mkdocs serve # live preview at http://127.0.0.1:8000
|
|
31
|
+
mkdocs build --strict # fail on warnings
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`--strict` runs in CI via `.github/workflows/docs.yml`.
|
|
35
|
+
|
|
36
|
+
## ADRs
|
|
37
|
+
|
|
38
|
+
Architecture Decision Records follow the Nygard format. Each ADR gets a
|
|
39
|
+
number (0001, 0002, ...) and a kebab-case title. Add new ADRs to
|
|
40
|
+
`docs/architecture/adr/index.md`.
|
|
41
|
+
|
|
42
|
+
## Excluded paths
|
|
43
|
+
|
|
44
|
+
`mkdocs.yml` excludes `plans/**` and `superpowers/**` from the built
|
|
45
|
+
site. These are workspace-specific and not user-facing.
|
|
46
|
+
|
|
47
|
+
## Before pushing doc changes
|
|
48
|
+
|
|
49
|
+
Run `mkdocs build --strict` to catch broken links, missing pages, and
|
|
50
|
+
markdown errors.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# codexloop-quota-and-capacity (Antigravity mirror of `.claude/skills/codexloop-quota-and-capacity/SKILL.md`)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# codexloop quota and capacity
|
|
5
|
+
|
|
6
|
+
`QuotaExhausted` is a billing wall with no `resets_at` — it is **never
|
|
7
|
+
waitable** and always needs a human to top up. Rate-limit windows *are*
|
|
8
|
+
waitable.
|
|
9
|
+
|
|
10
|
+
## CapacityState variants
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
Available # green light
|
|
14
|
+
ThrottleExhausted # retry_after: timedelta | None
|
|
15
|
+
WindowExhausted # resets_at: datetime | None, window: str
|
|
16
|
+
QuotaExhausted # reason: str — TERMINAL, never waitable
|
|
17
|
+
AuthFailed # reason: str — TERMINAL
|
|
18
|
+
TransientBackendError # retry_after: timedelta | None
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`is_waitable(state)` returns `False` only for `QuotaExhausted` and
|
|
22
|
+
`AuthFailed`.
|
|
23
|
+
|
|
24
|
+
## Body-first classification
|
|
25
|
+
|
|
26
|
+
`classify(signals)` in `domain/classify.py` maps `TurnSignals` to a
|
|
27
|
+
`CapacityState` using this ladder:
|
|
28
|
+
|
|
29
|
+
1. **Body wins.** `error.code` / `error.type` outrank HTTP status.
|
|
30
|
+
2. Auth: `error.code` / `error.type` in `ErrorClass.AUTH`, or status 401
|
|
31
|
+
→ `AuthFailed`.
|
|
32
|
+
3. Quota: `error.code` / `error.type` in `ErrorClass.QUOTA` →
|
|
33
|
+
`QuotaExhausted` (terminal).
|
|
34
|
+
4. Window: `ErrorClass.WINDOW` → `WindowExhausted` with `resets_at` from
|
|
35
|
+
plan windows.
|
|
36
|
+
5. Throttle: `ErrorClass.THROTTLE` → `ThrottleExhausted` with
|
|
37
|
+
`retry_after`.
|
|
38
|
+
6. Transient: `ErrorClass.TRANSIENT` or status 5xx →
|
|
39
|
+
`TransientBackendError`.
|
|
40
|
+
7. Status 429 with no body marker → `WindowExhausted(resets_at=None)`.
|
|
41
|
+
8. Fallback: `Available`.
|
|
42
|
+
|
|
43
|
+
High `used_percent` never outranks a billing marker. Completion claims
|
|
44
|
+
never outrank capacity rejection.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# codexloop-releasing (Antigravity mirror of `.claude/skills/codexloop-releasing/SKILL.md`)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# codexloop releasing
|
|
5
|
+
|
|
6
|
+
## Gitflow
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
develop (default branch) → main (releases)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
1. Feature PRs squash into `develop`.
|
|
13
|
+
2. `develop` merge-commits into `main` when ready to release.
|
|
14
|
+
3. release-please opens a PR on `main` when it detects conventional
|
|
15
|
+
commits since the last tag.
|
|
16
|
+
4. Merging the release PR publishes to PyPI.
|
|
17
|
+
|
|
18
|
+
## Workflows
|
|
19
|
+
|
|
20
|
+
- **`ci.yml`**: runs on every push. Tests, linting, type checks, per-layer
|
|
21
|
+
coverage, import-linter, bandit, pip-audit.
|
|
22
|
+
- **`publish-testpypi.yml`**: publishes to TestPyPI on every `develop`
|
|
23
|
+
push. **Known gap: no test gate.** This workflow builds and publishes
|
|
24
|
+
without running tests first. It trusts that CI passed.
|
|
25
|
+
- **`release-please.yml`**: opens a release PR on `main` when conventional
|
|
26
|
+
commits land. Merging that PR publishes to PyPI (via GitHub Actions +
|
|
27
|
+
PyPI trusted publishing).
|
|
28
|
+
|
|
29
|
+
## release-please
|
|
30
|
+
|
|
31
|
+
Configured in `release-please-config.json`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"packages": {
|
|
36
|
+
".": {
|
|
37
|
+
"release-type": "python",
|
|
38
|
+
"package-name": "codexloop",
|
|
39
|
+
"changelog-path": "CHANGELOG.md"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
It maintains `CHANGELOG.md` automatically from conventional commits.
|
|
46
|
+
|
|
47
|
+
## Before merging develop → main
|
|
48
|
+
|
|
49
|
+
1. All CI gates green on `develop`.
|
|
50
|
+
2. TestPyPI publish succeeded (workflow run shows no errors).
|
|
51
|
+
3. CHANGELOG.md reflects all user-facing changes since last release.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# codexloop-rest-surface (Antigravity mirror of `.claude/skills/codexloop-rest-surface/SKILL.md`)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# codexloop REST surface
|
|
5
|
+
|
|
6
|
+
`codexloop api` generates a 1:1 CLI mirror of the OpenAI Python SDK's REST
|
|
7
|
+
methods. The tree is code-generated from the SDK itself, not maintained by
|
|
8
|
+
hand.
|
|
9
|
+
|
|
10
|
+
## api_baseline.json drift gate
|
|
11
|
+
|
|
12
|
+
`api_baseline.json` freezes the **OpenAI SDK's** method count and list,
|
|
13
|
+
not codexloop's. It is a snapshot of what the SDK exposes, not what
|
|
14
|
+
codexloop adds.
|
|
15
|
+
|
|
16
|
+
A test fails when:
|
|
17
|
+
|
|
18
|
+
- The SDK adds or removes methods (changes `method_count`).
|
|
19
|
+
- Methods appear or disappear from the `methods` list.
|
|
20
|
+
- `local_helpers` (like `chat.completions.stream`) change.
|
|
21
|
+
|
|
22
|
+
This gate prevents SDK upgrades from silently altering the API surface
|
|
23
|
+
without review.
|
|
24
|
+
|
|
25
|
+
## Refreshing the baseline
|
|
26
|
+
|
|
27
|
+
After an intentional SDK upgrade, refresh the baseline:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
codexloop api --help # generates the tree
|
|
31
|
+
# The test writes the new baseline to api_baseline.json
|
|
32
|
+
pytest tests/cli/test_api.py --update-baseline
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Commit the updated `api_baseline.json` in the same PR as
|
|
36
|
+
`pyproject.toml` changes.
|
|
37
|
+
|
|
38
|
+
**Never refresh the baseline for unrelated PRs.** A drift failure
|
|
39
|
+
during SDK upgrade is expected and intentional — it forces you to
|
|
40
|
+
inspect what changed.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# codexloop-testing (Antigravity mirror of `.claude/skills/codexloop-testing/SKILL.md`)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# codexloop testing
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
tests/domain/ # pure unit + Hypothesis
|
|
8
|
+
tests/application/ # fakes for every port (application/interfaces/)
|
|
9
|
+
tests/infrastructure/ # adapters
|
|
10
|
+
tests/shim/ # fake_codex.py + fake_appserver.py — executable fakes
|
|
11
|
+
tests/live/ # marker: live — real OpenAI account, opt-in
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pytest # skips live (default -m "not live and not system")
|
|
16
|
+
pytest -m system # real FS/git + scripted agent
|
|
17
|
+
pytest -m live # needs OPENAI_API_KEY
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **Fakes over mocks.** Implement the `Protocol`; `mypy --strict` checks it.
|
|
21
|
+
- **Executable shims.** `fake_codex.py` and `fake_appserver.py` in
|
|
22
|
+
`tests/shim/` are genuine executables driven by env vars — the only such
|
|
23
|
+
fakes in the fleet. They let you script exact output sequences without
|
|
24
|
+
touching the OpenAI SDK.
|
|
25
|
+
- **No `time.sleep` in tests.** `FakeClock` / `FakeSleeper`.
|
|
26
|
+
- Hypothesis for numeric / time-based invariants.
|
|
27
|
+
- `# pragma: no cover` needs a reason.
|
|
28
|
+
- **100% branch coverage per layer**, enforced by gate:
|
|
29
|
+
```bash
|
|
30
|
+
pytest -q --cov=codexloop.domain --cov-fail-under=100
|
|
31
|
+
pytest -q --cov=codexloop.application --cov-fail-under=100
|
|
32
|
+
pytest -q --cov=codexloop.infrastructure --cov-fail-under=100
|
|
33
|
+
pytest -q --cov=codexloop.cli --cov-fail-under=100
|
|
34
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codexloop-architecture
|
|
3
|
+
description: Onion layers for codexloop (domain, application, infrastructure, cli), import-linter, and where new code belongs. Use before adding a module under src/codexloop/.
|
|
4
|
+
allowed-tools: Read Grep Glob Bash(lint-imports)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codexloop architecture
|
|
8
|
+
|
|
9
|
+
Dependencies point inward. `import-linter` enforces this in CI.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
src/codexloop/
|
|
13
|
+
├── domain/ # PURE. stdlib only. No I/O, no async, no third-party.
|
|
14
|
+
├── application/ # Protocol ports + use cases. Imports domain + stdlib.
|
|
15
|
+
├── infrastructure/ # Adapters. ONLY layer that may import openai.
|
|
16
|
+
├── cli/ # Typer. Calls application via bootstrap.
|
|
17
|
+
└── bootstrap.py # Composition root — the ONE module that sees every layer.
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Where does new code go?
|
|
21
|
+
|
|
22
|
+
1. Touches FS, network, clock, or an SDK? → `infrastructure/`, behind a
|
|
23
|
+
`Protocol` in `application/interfaces/`. Never `import openai`
|
|
24
|
+
elsewhere.
|
|
25
|
+
2. Pure decision, zero I/O? → `domain/`. Examples: `classify.py`,
|
|
26
|
+
`waiting.py`, `completion.py`, `capacity.py`.
|
|
27
|
+
3. Orchestration (port → domain → port)? → `application/`.
|
|
28
|
+
4. Argument parsing / terminal formatting? → `cli/`.
|
|
29
|
+
|
|
30
|
+
When in doubt, push logic inward. `lint-imports` names the broken contract.
|
|
31
|
+
|
|
32
|
+
See ADR 0001.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codexloop-completion
|
|
3
|
+
description: Three-layer completion evaluation (structured output → marker → continue), capacity rejection outranks completion claim. Use when working with completion.py.
|
|
4
|
+
allowed-tools: Read Grep
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codexloop completion detection
|
|
8
|
+
|
|
9
|
+
`CompletionEvaluator` in `domain/completion.py` maps `TurnSignals` +
|
|
10
|
+
`CapacityState` into a `CompletionVerdict`.
|
|
11
|
+
|
|
12
|
+
## CompletionVerdict variants
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
Done # task fully complete
|
|
16
|
+
Continue # remaining: list[str]
|
|
17
|
+
Blocked # reason: str — human intervention needed
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Three-layer evaluation
|
|
21
|
+
|
|
22
|
+
**Capacity rejection always outranks a completion claim.** If the turn is
|
|
23
|
+
not `Available`, return `Continue` regardless of structured output or
|
|
24
|
+
markers.
|
|
25
|
+
|
|
26
|
+
For an `Available` turn, the layers are:
|
|
27
|
+
|
|
28
|
+
1. **Structured output** (`StructuredOutput` tool call result):
|
|
29
|
+
- `blocked_on` set → `Blocked(reason=blocked_on)`.
|
|
30
|
+
- `complete: true` and `remaining_work: []` → `Done`.
|
|
31
|
+
- `complete: true` but `remaining_work` not empty → `Continue`.
|
|
32
|
+
- `complete: false` → `Continue(remaining=remaining_work)`.
|
|
33
|
+
|
|
34
|
+
2. **Marker on own line**: if `CODEXLOOP_TASK_FULLY_COMPLETE` appears on
|
|
35
|
+
its own line in the final message → `Done`.
|
|
36
|
+
|
|
37
|
+
3. **Fallback**: `Continue(remaining=[])`.
|
|
38
|
+
|
|
39
|
+
The marker check uses `_marker_on_own_line(message, done_marker)`: the
|
|
40
|
+
string must be the only non-whitespace on the line. Partial matches or
|
|
41
|
+
inline occurrences do not count.
|
|
42
|
+
|
|
43
|
+
**Never treat a non-`Available` turn as `Done`.** Rate-limit windows and
|
|
44
|
+
transient errors are waitable; completion is deferred until capacity
|
|
45
|
+
returns.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codexloop-docs
|
|
3
|
+
description: mkdocs-material, ADR structure, and doc build/serve commands. Use when adding documentation.
|
|
4
|
+
allowed-tools: Read Bash(mkdocs *)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codexloop docs
|
|
8
|
+
|
|
9
|
+
User-facing documentation lives in `docs/` and is built with
|
|
10
|
+
mkdocs-material.
|
|
11
|
+
|
|
12
|
+
## Structure
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
docs/
|
|
16
|
+
├── index.md # landing page
|
|
17
|
+
├── getting-started.md
|
|
18
|
+
├── guides/ # how-to guides
|
|
19
|
+
│ └── rest-api-surface.md
|
|
20
|
+
├── architecture/
|
|
21
|
+
│ ├── index.md
|
|
22
|
+
│ └── adr/ # Architecture Decision Records
|
|
23
|
+
│ ├── index.md
|
|
24
|
+
│ ├── 0001-onion-import-linter.md
|
|
25
|
+
│ └── 0002-subprocess-codex-exec.md
|
|
26
|
+
├── contributing.md
|
|
27
|
+
└── publishing.md
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
mkdocs serve # live preview at http://127.0.0.1:8000
|
|
34
|
+
mkdocs build --strict # fail on warnings
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`--strict` runs in CI via `.github/workflows/docs.yml`.
|
|
38
|
+
|
|
39
|
+
## ADRs
|
|
40
|
+
|
|
41
|
+
Architecture Decision Records follow the Nygard format. Each ADR gets a
|
|
42
|
+
number (0001, 0002, ...) and a kebab-case title. Add new ADRs to
|
|
43
|
+
`docs/architecture/adr/index.md`.
|
|
44
|
+
|
|
45
|
+
## Excluded paths
|
|
46
|
+
|
|
47
|
+
`mkdocs.yml` excludes `plans/**` and `superpowers/**` from the built
|
|
48
|
+
site. These are workspace-specific and not user-facing.
|
|
49
|
+
|
|
50
|
+
## Before pushing doc changes
|
|
51
|
+
|
|
52
|
+
Run `mkdocs build --strict` to catch broken links, missing pages, and
|
|
53
|
+
markdown errors.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codexloop-quota-and-capacity
|
|
3
|
+
description: QuotaExhausted has no reset field, body-first classification ladder, and waitability rules. Use when working with capacity.py or classify.py.
|
|
4
|
+
allowed-tools: Read Grep
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codexloop quota and capacity
|
|
8
|
+
|
|
9
|
+
`QuotaExhausted` is a billing wall with no `resets_at` — it is **never
|
|
10
|
+
waitable** and always needs a human to top up. Rate-limit windows *are*
|
|
11
|
+
waitable.
|
|
12
|
+
|
|
13
|
+
## CapacityState variants
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
Available # green light
|
|
17
|
+
ThrottleExhausted # retry_after: timedelta | None
|
|
18
|
+
WindowExhausted # resets_at: datetime | None, window: str
|
|
19
|
+
QuotaExhausted # reason: str — TERMINAL, never waitable
|
|
20
|
+
AuthFailed # reason: str — TERMINAL
|
|
21
|
+
TransientBackendError # retry_after: timedelta | None
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`is_waitable(state)` returns `False` only for `QuotaExhausted` and
|
|
25
|
+
`AuthFailed`.
|
|
26
|
+
|
|
27
|
+
## Body-first classification
|
|
28
|
+
|
|
29
|
+
`classify(signals)` in `domain/classify.py` maps `TurnSignals` to a
|
|
30
|
+
`CapacityState` using this ladder:
|
|
31
|
+
|
|
32
|
+
1. **Body wins.** `error.code` / `error.type` outrank HTTP status.
|
|
33
|
+
2. Auth: `error.code` / `error.type` in `ErrorClass.AUTH`, or status 401
|
|
34
|
+
→ `AuthFailed`.
|
|
35
|
+
3. Quota: `error.code` / `error.type` in `ErrorClass.QUOTA` →
|
|
36
|
+
`QuotaExhausted` (terminal).
|
|
37
|
+
4. Window: `ErrorClass.WINDOW` → `WindowExhausted` with `resets_at` from
|
|
38
|
+
plan windows.
|
|
39
|
+
5. Throttle: `ErrorClass.THROTTLE` → `ThrottleExhausted` with
|
|
40
|
+
`retry_after`.
|
|
41
|
+
6. Transient: `ErrorClass.TRANSIENT` or status 5xx →
|
|
42
|
+
`TransientBackendError`.
|
|
43
|
+
7. Status 429 with no body marker → `WindowExhausted(resets_at=None)`.
|
|
44
|
+
8. Fallback: `Available`.
|
|
45
|
+
|
|
46
|
+
High `used_percent` never outranks a billing marker. Completion claims
|
|
47
|
+
never outrank capacity rejection.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codexloop-releasing
|
|
3
|
+
description: release-please, gitflow (develop → main), TestPyPI + PyPI publishing, and the known publish-testpypi gap. Use before cutting a release.
|
|
4
|
+
allowed-tools: Read Bash(git *)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codexloop releasing
|
|
8
|
+
|
|
9
|
+
## Gitflow
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
develop (default branch) → main (releases)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
1. Feature PRs squash into `develop`.
|
|
16
|
+
2. `develop` merge-commits into `main` when ready to release.
|
|
17
|
+
3. release-please opens a PR on `main` when it detects conventional
|
|
18
|
+
commits since the last tag.
|
|
19
|
+
4. Merging the release PR publishes to PyPI.
|
|
20
|
+
|
|
21
|
+
## Workflows
|
|
22
|
+
|
|
23
|
+
- **`ci.yml`**: runs on every push. Tests, linting, type checks, per-layer
|
|
24
|
+
coverage, import-linter, bandit, pip-audit.
|
|
25
|
+
- **`publish-testpypi.yml`**: publishes to TestPyPI on every `develop`
|
|
26
|
+
push. **Known gap: no test gate.** This workflow builds and publishes
|
|
27
|
+
without running tests first. It trusts that CI passed.
|
|
28
|
+
- **`release-please.yml`**: opens a release PR on `main` when conventional
|
|
29
|
+
commits land. Merging that PR publishes to PyPI (via GitHub Actions +
|
|
30
|
+
PyPI trusted publishing).
|
|
31
|
+
|
|
32
|
+
## release-please
|
|
33
|
+
|
|
34
|
+
Configured in `release-please-config.json`:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"packages": {
|
|
39
|
+
".": {
|
|
40
|
+
"release-type": "python",
|
|
41
|
+
"package-name": "codexloop",
|
|
42
|
+
"changelog-path": "CHANGELOG.md"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
It maintains `CHANGELOG.md` automatically from conventional commits.
|
|
49
|
+
|
|
50
|
+
## Before merging develop → main
|
|
51
|
+
|
|
52
|
+
1. All CI gates green on `develop`.
|
|
53
|
+
2. TestPyPI publish succeeded (workflow run shows no errors).
|
|
54
|
+
3. CHANGELOG.md reflects all user-facing changes since last release.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codexloop-rest-surface
|
|
3
|
+
description: Generated `codexloop api` command tree, api_baseline.json drift gate, and how to refresh the baseline. Use when SDK upgrades change the surface.
|
|
4
|
+
allowed-tools: Read Bash(codexloop api *)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codexloop REST surface
|
|
8
|
+
|
|
9
|
+
`codexloop api` generates a 1:1 CLI mirror of the OpenAI Python SDK's REST
|
|
10
|
+
methods. The tree is code-generated from the SDK itself, not maintained by
|
|
11
|
+
hand.
|
|
12
|
+
|
|
13
|
+
## api_baseline.json drift gate
|
|
14
|
+
|
|
15
|
+
`api_baseline.json` freezes the **OpenAI SDK's** method count and list,
|
|
16
|
+
not codexloop's. It is a snapshot of what the SDK exposes, not what
|
|
17
|
+
codexloop adds.
|
|
18
|
+
|
|
19
|
+
A test fails when:
|
|
20
|
+
|
|
21
|
+
- The SDK adds or removes methods (changes `method_count`).
|
|
22
|
+
- Methods appear or disappear from the `methods` list.
|
|
23
|
+
- `local_helpers` (like `chat.completions.stream`) change.
|
|
24
|
+
|
|
25
|
+
This gate prevents SDK upgrades from silently altering the API surface
|
|
26
|
+
without review.
|
|
27
|
+
|
|
28
|
+
## Refreshing the baseline
|
|
29
|
+
|
|
30
|
+
After an intentional SDK upgrade, refresh the baseline:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
codexloop api --help # generates the tree
|
|
34
|
+
# The test writes the new baseline to api_baseline.json
|
|
35
|
+
pytest tests/cli/test_api.py --update-baseline
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Commit the updated `api_baseline.json` in the same PR as
|
|
39
|
+
`pyproject.toml` changes.
|
|
40
|
+
|
|
41
|
+
**Never refresh the baseline for unrelated PRs.** A drift failure
|
|
42
|
+
during SDK upgrade is expected and intentional — it forces you to
|
|
43
|
+
inspect what changed.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codexloop-testing
|
|
3
|
+
description: Fakes over mocks, FakeClock/FakeSleeper, Hypothesis, pytest markers, per-layer 100% coverage. Consult before adding tests or mocks.
|
|
4
|
+
allowed-tools: Read Grep Glob Bash(pytest *)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codexloop testing
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
tests/domain/ # pure unit + Hypothesis
|
|
11
|
+
tests/application/ # fakes for every port (application/interfaces/)
|
|
12
|
+
tests/infrastructure/ # adapters
|
|
13
|
+
tests/shim/ # fake_codex.py + fake_appserver.py — executable fakes
|
|
14
|
+
tests/live/ # marker: live — real OpenAI account, opt-in
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pytest # skips live (default -m "not live and not system")
|
|
19
|
+
pytest -m system # real FS/git + scripted agent
|
|
20
|
+
pytest -m live # needs OPENAI_API_KEY
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- **Fakes over mocks.** Implement the `Protocol`; `mypy --strict` checks it.
|
|
24
|
+
- **Executable shims.** `fake_codex.py` and `fake_appserver.py` in
|
|
25
|
+
`tests/shim/` are genuine executables driven by env vars — the only such
|
|
26
|
+
fakes in the fleet. They let you script exact output sequences without
|
|
27
|
+
touching the OpenAI SDK.
|
|
28
|
+
- **No `time.sleep` in tests.** `FakeClock` / `FakeSleeper`.
|
|
29
|
+
- Hypothesis for numeric / time-based invariants.
|
|
30
|
+
- `# pragma: no cover` needs a reason.
|
|
31
|
+
- **100% branch coverage per layer**, enforced by gate:
|
|
32
|
+
```bash
|
|
33
|
+
pytest -q --cov=codexloop.domain --cov-fail-under=100
|
|
34
|
+
pytest -q --cov=codexloop.application --cov-fail-under=100
|
|
35
|
+
pytest -q --cov=codexloop.infrastructure --cov-fail-under=100
|
|
36
|
+
pytest -q --cov=codexloop.cli --cov-fail-under=100
|
|
37
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codexloop-architecture
|
|
3
|
+
description: Onion layers for codexloop (domain, application, infrastructure, cli), import-linter, and where new code belongs. Use before adding a module under src/codexloop/.
|
|
4
|
+
allowed-tools: Read Grep Glob Bash(lint-imports)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# codexloop architecture
|
|
8
|
+
|
|
9
|
+
Dependencies point inward. `import-linter` enforces this in CI.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
src/codexloop/
|
|
13
|
+
├── domain/ # PURE. stdlib only. No I/O, no async, no third-party.
|
|
14
|
+
├── application/ # Protocol ports + use cases. Imports domain + stdlib.
|
|
15
|
+
├── infrastructure/ # Adapters. ONLY layer that may import openai.
|
|
16
|
+
├── cli/ # Typer. Calls application via bootstrap.
|
|
17
|
+
└── bootstrap.py # Composition root — the ONE module that sees every layer.
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Where does new code go?
|
|
21
|
+
|
|
22
|
+
1. Touches FS, network, clock, or an SDK? → `infrastructure/`, behind a
|
|
23
|
+
`Protocol` in `application/interfaces/`. Never `import openai`
|
|
24
|
+
elsewhere.
|
|
25
|
+
2. Pure decision, zero I/O? → `domain/`. Examples: `classify.py`,
|
|
26
|
+
`waiting.py`, `completion.py`, `capacity.py`.
|
|
27
|
+
3. Orchestration (port → domain → port)? → `application/`.
|
|
28
|
+
4. Argument parsing / terminal formatting? → `cli/`.
|
|
29
|
+
|
|
30
|
+
When in doubt, push logic inward. `lint-imports` names the broken contract.
|
|
31
|
+
|
|
32
|
+
See ADR 0001.
|