8000 ROADMAP §3.6 + §4.9: model routes, HostAdapter, portable .aladdin/config.yaml by adeelahmad · Pull Request #14 · adeelahmad/aloop · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
8000
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ tag in lockstep. Tag releases as `vMAJOR.MINOR.PATCH`.
overrides it (`breach:` line) and acts per class (SKILL.md "Breach classes").

### Added
- **Portable project config** (ROADMAP §4.9). `.aladdin/config.yaml` (budgets, context,
semantic, models, provenance, memory, git, plus per-role budget profiles) replaces
`docs/agents/defaults.md` as the human's file. `aloop config migrate` writes it from the
current settings; when it exists it is authoritative and defaults.md is rendered from it
(`config sync`, run by SessionStart and `aloop-init --apply`), so the bash gates read the
same values. `config check` refuses unknown sections, keys, bad numbers and schema.
- **The `HostAdapter` interface** (ROADMAP §4.9). `_host.py` names, per host, the one place
the harness does each thing: capabilities, the dispatch vocabulary (spawn / wait / send /
cancel / subscribe), model and transcript resolution, usage and context pressure,
compact / rotate, the worktree seam, gate install and enforcement, tool policy. Claude
Code is the reference; Codex and OpenCode are adapters. `aloop host describe` prints the
contract, `host check` (in `make test-hosts`) fails if an adapter is incomplete or would
enforce a gate as a warning. What a host lacks raises `Unsupported`; nothing is faked.
- **Abstract model routes** (ROADMAP §3.6). Roles map to `strongest | strong | economical`
and each host resolves the route to its own id (`plugin/hosts/routes.json`; Codex: the
session's model). `AGENTIC_MODEL_PLANNING` / `AGENTIC_MODEL_WORKER` default to
`strongest` / `strong`; a pinned id still passes through. `aloop models resolve` is what
the supervisor passes on dispatch and what `aloop install` renders; `--json` makes the
route observable (conformance 18).
- **Sprint token envelope and admission control** (ROADMAP §4.2). `budget admit <task>`
runs before each dispatch. At `SPRINT_TOKENS_SOFT` it prints one rollover warning, and
at `SPRINT_TOKENS_HARD` admission closes (exit 3). Refused tasks are carried over in
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ shellcheck: ## Lint gate scripts (skipped with a warning if shellcheck is absent
.PHONY: pycheck
pycheck: ## Byte-compile the Python hook scripts
@python3 -m py_compile plugin/bin/budget plugin/bin/stats plugin/bin/aloop-init plugin/bin/gate-commit-author plugin/bin/_paths.py \
plugin/bin/_usage.py plugin/bin/_events.py plugin/bin/events plugin/bin/intent plugin/bin/lessons plugin/bin/ids plugin/bin/targets plugin/bin/impact plugin/bin/timebox plugin/bin/sweep plugin/bin/capabilities plugin/bin/_lsp.py scripts/test/fixtures/fake-lsp.py plugin/bin/_timebox.py plugin/bin/host-adapter plugin/bin/install \
plugin/bin/_usage.py plugin/bin/_events.py plugin/bin/events plugin/bin/intent plugin/bin/lessons plugin/bin/ids plugin/bin/targets plugin/bin/impact plugin/bin/timebox plugin/bin/sweep plugin/bin/capabilities plugin/bin/models plugin/bin/_routes.py plugin/bin/host plugin/bin/_host.py plugin/bin/config plugin/bin/_config.py plugin/bin/_lsp.py scripts/test/fixtures/fake-lsp.py plugin/bin/_timebox.py plugin/bin/host-adapter plugin/bin/install \
&& rm -rf plugin/bin/__pycache__ && echo "python ok"

.PHONY: test-hosts
Expand All @@ -100,6 +100,10 @@ test-hosts: ## Budget/worktree regression + Codex hooks emulation + OpenCode plu
python3 scripts/test/orchestrator-sweep.test.py
python3 scripts/test/lsp-adapter.test.py
python3 scripts/test/context-boundary.test.py
python3 scripts/test/model-routes.test.py
python3 scripts/test/host-adapter.test.py
python3 scripts/test/portable-config.test.py
plugin/bin/host check
python3 scripts/test/targets-scope.test.py
python3 scripts/check-tracking.py
python3 scripts/test/codex-hooks.test.py
Expand Down
3 changes: 2 additions & 1 deletion docs/OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ The model can ignore a skill; it cannot ignore an exit code.

| Artifact | Scope | Purpose |
|---|---|---|
| `docs/agents/defaults.md` | project | Limits, models, author — set at init |
| `.aladdin/config.yaml` | project | The portable config (budgets, context, semantic, models, provenance, git); authoritative when present (`aloop config migrate`) |
| `docs/agents/defaults.md` | project | Limits, models, author — set at init, or rendered from `.aladdin/config.yaml` |
| `docs/agents/memory.md` | project | Lessons promoted by the retrospective; injected into contracts |
| `stories.md`, `sprintN/plan.md` | sprint | Stage-1 contract |
| `sN-NN-<slug>/{tasks,validate,plan}.md` | story | Stage-2 plan |
Expand Down
8 changes: 4 additions & 4 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ These parts already exist but their semantics must change:

Replacement tasks must fit the normal time and token budgets, and vertical slices are preferred.
5. **No post-kill narration.** ✅ *Done (Unreleased): the hook stops the worker on the crossing call and the harness writes the `status: re-plan` block. Test: `scripts/test/budget-hard-stop.test.py`.* After the hard stop, no model tokens are spent asking the worker for a report.
6. **Abstract model routes.** Roles map to `strongest | strong | economical`, and each host resolves those to concrete IDs.
6. **Abstract model routes.** ✅ *Done (Unreleased): `bin/models` + `plugin/hosts/routes.json`; `AGENTIC_MODEL_*` default to routes; `aloop install` renders the host's id. Test: `scripts/test/model-routes.test.py`.* Roles map to `strongest | strong | economical`, and each host resolves those to concrete IDs.

## 4. New work

Expand Down Expand Up @@ -227,7 +227,7 @@ Results carry source refs and are attached to planning provenance. An unavailabl

### 4.9 Portable host core

- **Extract a `HostAdapter` interface:**
- ✅ *Done (Unreleased): `_host.py` + `bin/host` (Claude reference, Codex and OpenCode adapters; `host check` runs in CI). Test: `scripts/test/host-adapter.test.py`.* **Extract a `HostAdapter` interface:**
- `Capabilities`
- `Spawn` / `Wait` / `Send` / `Cancel`
- `Subscribe`
Expand All @@ -240,8 +240,8 @@ Results carry source refs and are attached to planning provenance. An unavailabl
Claude Code is the reference implementation, and Codex and OpenCode become adapters behind it.
- ✅ *Usage reporting done (Unreleased): classes apart, `accuracy` + `unreported` per attempt. Test: `scripts/test/budget-meter.test.py`.* **Usage reporting** distinguishes fresh input, output, cache read, cache write and current context against the context window. Estimated counts are marked `estimated`.
- **A host capability matrix** covering usage, compaction, live messaging (for the time soft-stop), lifecycle and gate blocking.
- **Gate equivalence:** if a host cannot block natively, the outer controller enforces it. Never downgrade BLOCK to WARN.
- **A portable project config** in `.aladdin/config.yaml` (budgets, context, semantic, models, provenance, memory), replacing `docs/agents/defaults.md` with a migration path.
- ✅ *Done (Unreleased): `enforce_gate` is `native` or `outer`; `host check` fails on anything else.* **Gate equivalence:** if a host cannot block natively, the outer controller enforces it. Never downgrade BLOCK to WARN.
- ✅ *Done (Unreleased): `bin/config` migrate/sync/check/show; config.yaml is authoritative, defaults.md is rendered from it. Test: `scripts/test/portable-config.test.py`.* **A portable project config** in `.aladdin/config.yaml` (budgets, context, semantic, models, provenance, memory), replacing `docs/agents/defaults.md` with a migration path.
- **Host swap:** a sprint started on one host resumes on another from portable state.

### 4.10 Metrics
Expand Down
7 changes: 4 additions & 3 deletions docs/TRACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Status of every requirement in [ROADMAP.md](ROADMAP.md), in the four states §8
| 3.3 | Every model role metered against a role profile (non-workers observe-only) | IMPLEMENTED_TESTED | `scripts/test/budget-roles.test.py` |
| 3.4 | Hard breaches classified with evidence before acting | IMPLEMENTED_TESTED | `scripts/test/budget-classify.test.py` |
| 3.5 | No post-kill narration; harness writes the re-plan block | IMPLEMENTED_TESTED | `scripts/test/budget-hard-stop.test.py` |
| 3.6 | Abstract model routes | NOT_IMPLEMENTED | waits for PR #7 (install, init and host adapter) |
| 3.6 | Abstract model routes | IMPLEMENTED_TESTED | `scripts/test/model-routes.test.py` |
| 4.1 | Intent Contract with harness-issued ids | IMPLEMENTED_TESTED | `scripts/test/intent-contract.test.py` |
| 4.1 | Clarification policy (assume / default / ask / spike) | IMPLEMENTED_TESTED | `scripts/test/intent-contract.test.py` |
| 4.1 | Explicit autonomous mode | IMPLEMENTED_TESTED | `scripts/test/intent-contract.test.py` |
Expand All @@ -34,8 +34,9 @@ Status of every requirement in [ROADMAP.md](ROADMAP.md), in the four states §8
| 4.8 | Cross-project impact provider seam | IMPLEMENTED_TESTED | `scripts/test/impact-provider.test.py` |
| 4.9 | Usage reporting: classes apart, exact or marked, unreported classes named | IMPLEMENTED_TESTED | `scripts/test/budget-meter.test.py` |
| 4.9 | Host capability matrix | IMPLEMENTED_TESTED | `scripts/test/context-boundary.test.py` |
| 4.9 | HostAdapter interface extraction, gate equivalence (outer enforcement) | NOT_IMPLEMENTED | overlaps PR #7 |
| 4.9 | Portable `.aladdin/config.yaml`, host swap | NOT_IMPLEMENTED | overlaps PR #7 |
| 4.9 | HostAdapter interface extraction, gate equivalence (outer enforcement) | IMPLEMENTED_TESTED | `scripts/test/host-adapter.test.py` |
| 4.9 | Portable `.aladdin/config.yaml` with migration | IMPLEMENTED_TESTED | `scripts/test/portable-config.test.py` |
| 4.9 | Host swap: resume a sprint on another host from portable state | NOT_IMPLEMENTED | needs a two-host run |
| 4.10 | Metrics table (percentiles, breaches, waste, rates, cache, context peak) | IMPLEMENTED_TESTED | `scripts/test/metrics.test.py` |
| 4.10 | Commits, tests added, time soft limits, semantic context served | IMPLEMENTED_TESTED | `scripts/test/metrics.test.py` |
| 4.10 | Rotations / compactions | IMPLEMENTED_TESTED | `scripts/test/metrics.test.py` |
Expand Down
3 changes: 3 additions & 0 deletions plugin/bin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ ctx-symbols) → WARN + grep fallback; never a silent pass.
| `md-db`, `ctx-symbols` | PATH shims (this `bin/` is on PATH in every session): exec the real binary, building it once if needed. Gates test presence with `ensure-tools --resolve`, never `command -v` |
| `aloop` | the one entrypoint: `aloop <tool> …` (`aloop --list`); `aloop init` = aloop-init, `aloop install` = install. Linked into `~/.local/bin` for Codex/OpenCode |
| `install` | `aloop install codex\|opencode [--project DIR\|--global] [--uninstall]` — renders agents/skills/commands (+ the OpenCode plugin from `hosts/opencode/`) from this plugin's sources; `status`; `claude` |
| `host` | the `HostAdapter` interface (ROADMAP §4.9, `_host.py`): Claude Code is the reference, Codex and OpenCode are adapters. `describe [--host H] [--json]` prints the contract (capabilities, spawn/wait/send/cancel/subscribe vocabulary, compact/rotate, isolation, gate install/enforcement, tool policy, resolved models) · `check` (every adapter complete; gates `native` or `outer`, never warn; in `make test-hosts`) · `worktree create\|remove\|diff TASK`. A capability a host lacks raises `Unsupported`, never a guess. |
| `host-adapter` | SubagentStart/Stop: register sub-agents (`.agentic/state/agents/`) for the ledger; PreToolUse on Codex: a worker must `aloop bind <worktree>` first, then its commands/patches must stay in it (denied otherwise) |
| `task-worktree` | `add\|path\|remove <TASK_ID>` — per-task worktree at `.agentic/worktrees/<TASK_ID>` (branch `agentic/<TASK_ID>`) for hosts without built-in isolation |
| `bind` | a worker's first command on Codex/OpenCode: validates its worktree (`aloop bind <path>`) |
Expand All @@ -62,7 +63,9 @@ ctx-symbols) → WARN + grep fallback; never a silent pass.
| `ids` | canonical identity + referential integrity (ROADMAP §4.5): `freeze SPRINT_DIR` checks the structured references (story frontmatter ↔ dir, dirs ↔ stories.md, validate.md ↔ tasks.md headings, `split_from` → a retired task) and issues frozen UUIDv7 ids in `sprintN/ids.json`; a retired alias is never reused · `check` · `resolve ALIAS`. Run by `gate-stage2-complete`; events then carry `story_uid`/`task_uid`. |
| `targets` | semantic execution map, first slice (ROADMAP §4.6): the planner's `<story>/targets.json` change targets. `check STORY_DIR` (run by `gate-stage2-complete`) · `target Tn STORY_DIR` (file:line, or `planned` for the shadow workspace) · `shadow` · `scope Tn STORY_DIR` (the diff scope gate-green-verify derives when SCOPE_GLOBS is unset) · `context Cn --budget N` (just the target's definition) · `refs\|callers\|callees` via the LSP server in `AGENTIC_LSP_COMMAND` (`_lsp.py`: references + call hierarchy); no server or a missing capability → exit 4, never guessed. Backend: ctx-symbols, else a definition regex flagged weaker. |
| `impact` | optional cross-project impact provider seam (ROADMAP §4.8): `query consumers\|rationale\|migration\|standards SUBJECT [--task]` runs `AGENTIC_IMPACT_PROVIDER` (defaults.md or env), keeps only results with a `source_ref`, saves them under `docs/agents/impact/` and emits `artifact.created`. No provider or a failing one → WARN, exit 0. Builds no index. |
| `config` | the portable project config `.aladdin/config.yaml` (ROADMAP §4.9; sections budgets · context · semantic · models · provenance · memory · git, plus `budgets.roles.<role>`): `migrate` writes it from the current settings and renders `docs/agents/defaults.md` from it · `sync` re-renders defaults.md (SessionStart, `aloop-init --apply`) so bash readers see the same values · `check` validates (exit 2) · `show [--json]` effective value + source per setting. When it exists it is authoritative. |
| `capabilities` | the host capability matrix (`plugin/hosts/capabilities.json`: usage, compaction, live messaging, lifecycle, gate blocking; `unknown` = not advertised) · `boundary [HOST]` → rotate (default, the host's `/clear` or `/new`) or compact only when `AGENTIC_CONTEXT_BOUNDARY=compact` and advertised · `boundary --keep --reason` records the exception. SessionStart records `context.rotated` / `context.compacted`. |
| `models` | abstract model routes (ROADMAP §3.6): `resolve planning\|worker\|<role> [--host H] [--json]` → the host's id for the group's route (`strongest\|strong\|economical`, or a pinned id passed through; `--json` shows the route, conformance 18) · `routes` prints `hosts/routes.json`. `aloop install` renders from the same table. |
| `sweep` | the orchestrator loop's inspect step (ROADMAP §4.2): one action per planned task (DONE · ROLLOVER · WAIT · REPLAN · ACCEPT · RETRY · ESCALATE · SPAWN) from events, attempt states and rollover.md · `sweep wait --timeout S` blocks without a model call until something changes (exit 4 on timeout). |
| `timebox` | time budget `auto` mode (ROADMAP §4.2): with `AGENTIC_TIME_MODE=auto`, `propose --minutes N --min A --max B --reason` records one project task duration (once); `budget` then uses it (soft = 2/3); `revise --minutes N --reason --evidence REF…` only with existing evidence and within range · `show`. Task overrides still win. |
| `lessons` | planner lesson candidates (ROADMAP §4.3) in `docs/agents/lessons.jsonl`: `add --symptom --cause --change --applies-to --evidence [--task]` (refuses empty fields, no evidence, or a gate-weakening change) · `list` · `recurring` (groups seen in ≥ 2 tasks, as memory.md rows for the archivist) · `check-memory FILE` (run by `gate-memory`: a row citing `L-NNN` needs recurrence ≥ 2 tasks or a human `keep`). |
Expand Down
Loading
Loading
0