Conversation
…28 (F-28/F-29/F-30) (#143) * feat: per-entry provider records and the generic local-openai provider row Phase 1 provider axis (ADR-0028, F-28): providerEntries is six explicit records — capabilities live on each entry instead of being derived from identity comparisons, matching hostEntries; the five pre-existing providers are pinned deep-equal to the old construction. local-openai names any OpenAI-compatible server the user runs: billing local, no credentials, the openai-compatible transport only, projections ruflo/codex/opencode (no aqe — AQE's provider set is upstream's; no claude — that projection expects an anthropic-compatible surface), no discovery claims, no builtin bindings. Remote-endpoint policy pinned: local is a billing claim, not topology — https off-box is legal, plain http stays loopback-only. * feat: ak status names local non-AQE bindings plainly Phase 1 provider axis (F-29): when kit.json declares a binding whose provider is local and not AQE-projected, status prints one info row naming provider, host, and endpoint with the not-an-AQE-provider-type fact. Registry-driven (billing + projections), so ollama never triggers it and a future provider of the same shape gets the same treatment; no bindings means no new output. * docs: accept ADR-0028 with corrections; PROVIDERS.md gains the local-openai recipe Proposed by adrianco in PR #131; accepted with the api_mode citation annotated as invalid per Hermes v0.20.0 source and the ollama/local-openai AQE-projection asymmetry stated as an intentional decision. PROVIDERS.md adds a brief current-state section on declaring a local-openai binding.
…erification (#144) * docs: verify the current-state-only policy across the usage docs; fix the one leftover Sweep of USAGE-SCORECARD-METRICS, TRANSCRIPTS, and TROUBLESHOOTING against the house policy (main bodies describe current behavior; history lives in each doc's fix-history appendix). The corpus already complied except one inline before/after aside in TRANSCRIPTS §4.2, now a current-state sentence with the history moved to Appendix A. Doc-citation, markdownlint, and ga-surface gates green. * feat: quota labels hosts that have no quota surface instead of omitting them Phase 3 (F-10): readLimits gains an opt-in enabledHosts parameter and an 'others' list — every additional enabled registry-managed host appears as {supported: false, reason: 'no quota surface for this host'} so users can tell absent-by-design from broken. The two ADR-0010-sanctioned channels are unchanged; no new channel, no probe. The dashboard /api/limits payload now carries the labels (browser UI rendering is a noted follow-up).
…04/F-17/F-14) (#146) * feat: per-entry provider records and the generic local-openai provider row Phase 1 provider axis (ADR-0028, F-28): providerEntries is six explicit records — capabilities live on each entry instead of being derived from identity comparisons, matching hostEntries; the five pre-existing providers are pinned deep-equal to the old construction. local-openai names any OpenAI-compatible server the user runs: billing local, no credentials, the openai-compatible transport only, projections ruflo/codex/opencode (no aqe — AQE's provider set is upstream's; no claude — that projection expects an anthropic-compatible surface), no discovery claims, no builtin bindings. Remote-endpoint policy pinned: local is a billing claim, not topology — https off-box is legal, plain http stays loopback-only. * feat: ak status names local non-AQE bindings plainly Phase 1 provider axis (F-29): when kit.json declares a binding whose provider is local and not AQE-projected, status prints one info row naming provider, host, and endpoint with the not-an-AQE-provider-type fact. Registry-driven (billing + projections), so ollama never triggers it and a future provider of the same shape gets the same treatment; no bindings means no new output. * docs: accept ADR-0028 with corrections; PROVIDERS.md gains the local-openai recipe Proposed by adrianco in PR #131; accepted with the api_mode citation annotated as invalid per Hermes v0.20.0 source and the ollama/local-openai AQE-projection asymmetry stated as an intentional decision. PROVIDERS.md adds a brief current-state section on declaring a local-openai binding. * fix: the execution-adapter invariant no longer bricks ak at import for unwired routable hosts Phase 2 Wave 1 (F-01): the frozen adapter map's bidirectional import-time invariant becomes built-ins-one-directional (a built-in adapter wired to a non-routable or absent host still throws — the in-tree mistake), and executionAdapterFor() is the lookup seam externally-admitted adapters will join. A routable host with no adapter imports cleanly and degrades per worker via the pre-existing cli_unavailable path, which escalation ladders already step past. Built-in behavior byte-identical (same object references). * feat: host lifecycle is reached by registry lookup, uninstall runs the contract, permissions key by host Phase 2 Wave 1 (F-02, F-03, F-04): a lifecycle registry (construction- validated, one-way import) replaces the five OPENCODE_LIFECYCLE_ADAPTER named imports; source-text guards pin that no command names the adapter again. Uninstall tears down every lifecycle host through runLifecycle undo honoring ownership receipts — and persists nulled markers unconditionally like x/host.mjs always has (review-caught: gating the save on file changes stranded a stale mcp:ak receipt forever on the quiet-success path; regression test added). Setup's permission manifest unions auto-approve trust changes across ALL enabled hosts instead of claude's alone — byte-identical at the claude-only default; on opencode-enabled machines its four MCP tool-name rules join the disclosed/authorized set (stated tradeoff, disclosed pre-write by the trust manifest). * refactor: guidance targets derive from the host registry Phase 2 Wave 1 (F-17): guidanceTargets() loops nativeGuidance hosts and their legacy.guidanceFile instead of a closed four-name literal; output pinned byte-identical across every enablement combination. retiredForTarget stops force-stripping targets outside the derived universe. The generic fallback accepts id-shaped names only — a traversal-shaped guidanceFile contributes no target at all (review-caught latent escape; schema-level validation is the wave-4 admission gate's job). * feat: kit.json names its unknown top-level keys instead of ignoring them silently Phase 2 Wave 1 (F-14): loadKitConfig warns once per distinct unknown key-set on stderr — 'preserved, ignored' — so a future hostAdapters key on an older ak is a named no-op, not a silent one. Keys still round-trip untouched; clean configs warn nothing; legacy pre-migration shapes live nested inside recognized envelopes and never trip it.
…loop (#145) Phase 2 Wave 2 (F-05): the ~94-line inline opencode block becomes a per-host detail renderer registered in a dispatch table and invoked by an exported host-neutral loop — output byte-identical (whitespace-normalized diff clean), row order unchanged, the duplicate have('opencode') probe replaced by the integration-facts snapshot the surrounding loops already consume. Renderers carry their own error isolation by contract. The #129/#133 AQE-drift projection sharing and Phase 1's local-binding row are untouched, verified by their unchanged suites. A synthetic fourth host renders through the same loop with zero loop changes.
…h loop passes (#147) The wave-2 quality gate caught a JSDoc mismatch: renderHostDetailRows calls every renderer with hostId, which opencodeDetailRows' destructured signature did not declare. Typecheck green; suite unchanged.
…t-in-scoped (#148) Phase 2 Wave 3 (F-21, F-06): host-set and default-map pins across nine test files now derive from managedHostIds()/routableHostIds()/primaryHostIds()/ defaultHostMap() — scoped to built-ins where the assertion is about built-ins — so registering a host updates expectations exactly once. Deliberate literals that survive are policy pins (DEFAULT_PRIMARY_HOST) or fixture inputs, each commented. The component-directory parity invariant is stated built-in-scoped; externally-admitted adapters are exempt until the wave-4 contract graduates them. No production code changed; suite counts identical.
…mental) (#149) * feat: the host-adapter extension point — data plus consented subprocess hooks (ADR-0029) Phase 2 Wave 4: an external host adapter is a validated JSON manifest plus subprocess hooks; no third-party code ever runs in-process. Behind AK_EXPERIMENTAL_HOST_ADAPTERS=1 with byte-zero default behavior. - manifest.mjs: a STRICT allowlist at every level — top-level, host, install, legacy, capabilities, detection, driving, lifecycle, and trust reject any unknown key, so the structural caps (canBePrimary, aqeProvider, commandStatusline) and a path-traversal guidanceFile are inexpressible, not merely refused; an external adapter may not name an npm package ak would install, and its detection bin must be id-shaped. - admission.mjs: fail-closed, per-adapter isolated admission — validate, cap-check, contract match, builtin-shadow refusal, then a locale-independent content hash checked against hash-pinned consent (edit-invalidated). One bad entry never affects built-ins or siblings. - hook-runner.mjs: the only path external code executes — a supervised, shell-free subprocess with an env allowlist, process-group kill on timeout, and bounded capture. - consent.mjs: the 0600 hash-pinned trust store. - admitted.mjs: the frozen built-ins+admitted overlay; effectiveHostRegistry is HOST_REGISTRY by reference until something is admitted. - Built-in lifecycle loops iterate builtinHostsWithLifecycle() so an admitted host can never enter an opencode-shaped teardown before external lifecycle execution graduates. Security-reviewed (approve-with-nits, no RCE/escape/pollution): six of seven attacks held; the consent-forgery gap and the checklist-not-allowlist finding are closed by the strict allowlist above, verified against the reviewer's exploit probes. * test: adapter-door conformance harness, fixture adapter, and security regression corpus The graduation artifact ADR-0029 names: a real fixture adapter under tests/fixtures/adapters/acme with committed subprocess hooks, plus a black-box conformance harness that admits it through the real consent store, runs its declared hooks as real subprocesses against a marker file, and proves each negative-corpus manifest is refused with its exact named reason. Includes the allowlist, locale-hash, and consent-edit-invalidation regression tests that pin the closed security findings. * docs: ADR-0029 accepts the extension point; supersede ADR-0016's closed-registry clause ADR-0029 (Accepted, experimental contract) records the data-plus-hooks mechanism, credits @adrianco's PR #131 proposal and states what changed and why (grounded in the four-sweep research), carries a graded Working/Demo/TBD table, and formally supersedes ADR-0016's closed-registry clause — narrowly: kit.json may name a manifest, but nothing under it is ever imported in-process, so the clause's intent survives. PROVIDERS.md gains a brief current-state note. kit.json entry shape corrected to {name, source, contract} to match the code. * test: skip the POSIX-mode consent-file assertion on Windows NTFS carries no Unix permission bits, so mode & 0o777 never reflects the 0600 the consent store writes; the write still requests 0600. Guarded with the repo's { skip: process.platform === 'win32' } idiom (windows-only CI failure).
…set docs (D-2/F-25/F-26/F-27) (#150) * feat: host tiers and asymmetries render from capabilities, not host names Phase 3 (D-2, F-25, F-26): hostTierLabel() derives a plain phrase from a host's capabilities — 'drives sessions · can lead' for a primary host, 'routing only · supervised · not AQE' for a supervised one, 'routing only · external adapter' for an admitted external — replacing x/host.mjs's h.id === 'opencode' tier ternary. hostAsymmetryNote() states the cross-host MCP delegation bridge (F-25) and the permission consent boundary / absent backend env flag (F-26) from each host's own trust manifest and capability set. Both are true by construction for any host, built-in or admitted; no new tier jargon. * docs: the host set is the built-in set, not a closed universe Phase 3 (F-27): HOST-SUPPORT.md presents claude/codex/opencode as the built-in execution hosts and notes external adapters can extend the set behind the experimental flag (ADR-0029), rather than 'the three execution hosts'; ubiquitous-language defines host by registry capability with the three as built-in examples. Current-state voice; every factual row about the built-ins unchanged.
* docs: ADR-0031 ratifies the capability-graduation model and the upstream request path Accepts, as a governance decision, that external host adapters reach full parity by EARNING capabilities through tiered conformance plus an explicit maintainer grant — never by self-declaring them (that block stays permanent, the safety invariant from ADR-0029, which this amends). Records the two graduation destinations (blessed external, promoted built-in), the contributor-to-built-in lifecycle, and the upstream capability-request path: some ceilings aren't ak's to lift (AQE provider-type identity is agentic-qe's closed enum; native ruflo backend is ruflo's ENABLE_* model), so those become tracked requests with honest interim behavior. Carries a dated Working/Proposed self-grade — the machinery (trust CLI, external execution, tiered conformance, grant store) is staged, not built. ADR-0029 gets a matching amendment note. * docs: local snapshots of the three companion artifacts, each linking its origin The host-extensibility explainer (consumer + implementer walkthrough), the adapter-contract dossier (the research synthesis behind ADR-0029/0031), and the host/provider consistency master document now live in docs/ as self-contained HTML, each carrying an origin banner back to its live Claude artifact so the canonical source is one click away.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is the consolidated host/provider consistency program — every phase built, reviewed, and CI-verified as its own PR into
develop, assembled here for a single human review. It is left open deliberately; nothing here has been merged tomain.Origin: this began as an evaluation of @adrianco's PR #131 (proposed ADRs 0028/0029/0030 for Hermes/host-adapter support). We took ownership of the design, grounded it in a four-sweep research pass against live upstream source (ruflo, the three hosts, Hermes at HEAD, agentic-qe), and executed it in dependency order.
The eight increments
local-openaiprovider row; ADR-0028 accepted in-tree with corrections (F-28/F-29/F-30)ak statusrenders host detail through a host-neutral dispatch loop (F-05)How it was built
Each wave ran as a swarm of TDD builders with disjoint file ownership (one git author, zero merge conflicts), followed by effort/impact-scaled review — deep adversarial review on the load-bearing and security-critical changes, light review on mechanical ones, with a second verify pass on every major finding. Independent parallel tracks ran in git worktrees where file footprints didn't overlap.
The review layer caught and fixed real defects before merge, not after: a fail-open inversion of the qe-court collusion check; an actor-host misattribution in live events; an uninstall save-gate that stranded ownership markers; a latent path-traversal in the guidance fallback; and — in the extension point — a consent-forgery gap and a checklist-not-allowlist weakness that together could have reached an
npm installpostinstall. The security review (approve-with-nits, no RCE) ran seven named attacks; six held, the seventh was closed and pinned as a regression corpus.What the reviewer should know
AK_EXPERIMENTAL_HOST_ADAPTERS=1with byte-zero default behavior —effectiveHostRegistry()isHOST_REGISTRYby reference until an adapter is admitted. ADR-0029 carries a graded Working/Demo/TBD table; external lifecycle execution, atrustCLI verb, and npm/URL manifest sources are explicitly TBD for a later graduation.tests/fixtures/adapters/acmeproves the contract; a real Hermes adapter waits until the contract graduates (owned by its proposer).59 files, +4,716/−280. Full gate green on every merged commit (typecheck, lint, markdownlint, build, ~1,682 tests across macOS/Ubuntu/Windows × Node 22/24/26).
🤖 Generated with Claude Code