Skip to content
Merged

Dev #2845

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
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"plugins": [
{
"name": "genie",
"version": "5.260830.9",
"version": "5.260830.12",
"source": "./plugins/genie",
"description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, wish them into plans, make with parallel agents, ship as one team. A coding genie that grows with your project."
}
Expand Down
117 changes: 117 additions & 0 deletions .github/workflows/orca-plugin-ref.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
name: Orca Plugin Ref

# Republish `plugins/genie` as a standalone git ref that Orca can install.
#
# Orca installs a plugin from a git URL + ref (or a local folder) whose ROOT
# holds `orca-plugin.json`. This repository root can never be that tree:
#
# * the loader rejects any tree containing a symlink ("unsafe file path or
# symlink"), and `docs -> .docs-vendor/genie` is one;
# * it caps an install at 2000 files / 50 MB, and a dev checkout is ~14k files;
# * the manifest is nested at `plugins/genie/orca-plugin.json`, invisible to a
# git source, which only looks at the root.
#
# `plugins/genie` on its own is symlink-free, ~132 files / ~1.3 MB, and holds the
# manifest at its root — so this workflow publishes exactly that subtree:
#
# main -> refs/heads/orca-plugin (what orca-marketplace.json points at)
# dev -> refs/heads/orca-plugin-dev (pre-release channel)
#
# SECURITY: `contents: write` is scoped to this one job, which runs no package
# script, dependency installer, hook, or any executable from the checkout. Every
# command below is trusted workflow control; the only inputs are a git tree hash
# derived from the pushed commit and the repository's own ref state.
#
# The published ref is TREE-ONLY BY DESIGN: each publish is a fresh parentless
# root commit carrying just the subtree, force-pushed over the previous one. It
# has no history, shares no ancestry with main/dev, and must never be merged
# back. Orca pins the commit it fetched, so a rewritten ref cannot retroactively
# change an existing install.

on:
push:
branches: [main, dev]
paths:
- 'plugins/genie/**'
- '.github/workflows/orca-plugin-ref.yml'
# Manual repair path: republish the current branch tip's subtree.
workflow_dispatch:

permissions:
contents: read

# Serialize per source branch. Never cancel: a cancelled force-push could leave
# the plugin ref pointing at an older subtree than the branch it mirrors.
concurrency:
group: orca-plugin-ref-${{ github.ref }}
cancel-in-progress: false

jobs:
publish:
name: Publish plugins/genie subtree
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write # force-push the tree-only orca-plugin[-dev] ref
steps:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
# Only the pushed commit's tree is needed; the publish never walks
# history. Credentials are bound below with `gh auth setup-git` once
# the target ref has been resolved.
fetch-depth: 1
persist-credentials: false

- name: Publish the subtree ref
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
BRANCH: ${{ github.ref_name }}
GIT_AUTHOR_NAME: 'github-actions[bot]'
GIT_AUTHOR_EMAIL: 'github-actions[bot]@users.noreply.github.com'
GIT_COMMITTER_NAME: 'github-actions[bot]'
GIT_COMMITTER_EMAIL: 'github-actions[bot]@users.noreply.github.com'
shell: bash
run: |
set -euo pipefail

case "${BRANCH}" in
main) TARGET_REF=orca-plugin ;;
dev) TARGET_REF=orca-plugin-dev ;;
*)
echo "::error ::orca-plugin-ref.unexpected-branch ${BRANCH} has no plugin ref mapping"
exit 1
;;
esac

SOURCE_SHA="$(git rev-parse HEAD)"
SUBTREE="$(git rev-parse "HEAD:plugins/genie")"
echo "source ${BRANCH}@${SOURCE_SHA} subtree ${SUBTREE} -> refs/heads/${TARGET_REF}"

# Idempotent: skip when the published ref already carries this exact
# tree. A missing ref (first publish) is the only tolerated lookup
# failure — anything else must abort rather than force-push blind.
PUBLISHED_TREE=""
REF_STATUS=0
REF_SHA="$(gh api "repos/${REPO}/git/ref/heads/${TARGET_REF}" --jq '.object.sha' 2>/dev/null)" || REF_STATUS=$?
if [ "${REF_STATUS}" -eq 0 ] && [ -n "${REF_SHA}" ]; then
PUBLISHED_TREE="$(gh api "repos/${REPO}/git/commits/${REF_SHA}" --jq '.tree.sha')"
else
echo "refs/heads/${TARGET_REF} not published yet"
fi

if [ "${PUBLISHED_TREE}" = "${SUBTREE}" ]; then
echo "::notice ::orca-plugin-ref.unchanged refs/heads/${TARGET_REF} already carries subtree ${SUBTREE}"
exit 0
fi

# Parentless root commit whose ROOT IS `plugins/genie`. `commit-tree`
# writes no working tree and copies no history.
COMMIT="$(git commit-tree "${SUBTREE}" \
-m "chore(orca-plugin): plugins/genie subtree of ${BRANCH} ${SOURCE_SHA}")"

gh auth setup-git
# Force is structural, not a race override: every publish is a fresh
# root commit, so the ref can only ever be replaced, never advanced.
git push --force origin "${COMMIT}:refs/heads/${TARGET_REF}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prevent stale runs from force-pushing the plugin ref

When multiple pushes to the same source branch overlap, GitHub Actions concurrency groups serialize them but do not guarantee FIFO ordering, so an older workflow run can execute this unconditional force-push after a newer run and leave orca-plugin or orca-plugin-dev pointing at an obsolete subtree indefinitely. Before pushing, verify that SOURCE_SHA is still the current tip of BRANCH and skip stale runs.

Useful? React with 👍 / 👎.

echo "::notice ::orca-plugin-ref.published refs/heads/${TARGET_REF} = ${COMMIT} (subtree ${SUBTREE})"
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ Biome's `noExcessiveCognitiveComplexity` is set to `maxAllowedComplexity: 25` (w
- **Hook dispatch is provider-aware and fail-closed** — the plugin-local Codex launcher selects only the canonical `$GENIE_HOME/bin/genie`, preserves stdin/stdout/signals, bounds child time/output, validates event-specific JSON, and converts launch/timeout/schema failures into a reasoned deny. Claude dispatch remains a short-lived in-process `genie hook dispatch` fork. Neither path is a daemon.
- **`AskUserQuestion` is the one PreToolUse carve-out** — it is in `NON_INTERCEPTABLE_PRE_TOOL_USE_TOOLS` and MUST get an EMPTY response, not the neutral `{ decision: 'block' }` block form. Empirically CC consumes any additionalContext as the synthesized answer, so a fail-closed block would corrupt the inline picker. The fail-closed envelope special-cases this tool.
- **The product MCP server and its launchers are retired** — `genie mcp` is a stub that writes a stable diagnostic to stderr and exits 1; no plugin ships an MCP declaration or launcher, and `genie init` only retires proven Genie-owned historical routes (in `.mcp.json` it retires only the dead `genie mcp` entry — a genie binary with args exactly `["mcp"]` — backing the file up first and preserving every other server byte-for-byte; `genie doctor` warns while that entry is still present). The UI-owned `genie ui-bridge` is retired too — there is no Genie UI any more, the Orca integration is the only UI surface — so it is the same shape of stub (stable stderr diagnostic, exit 1), and its private transport (`mcp-server.ts`), tool registry (`mcp-tools.ts`), and change watcher (`bridge-watcher.ts`) are deleted.
- **The Orca plugin ships as a tree-only subtree ref, never from the repo root** — Orca installs a plugin from a git URL+ref (or a local folder) whose ROOT holds `orca-plugin.json`, and its loader rejects any tree containing a symlink ("unsafe file path or symlink") and caps an install at 2000 files / 50 MB. The genie root can therefore never be the install tree (`docs -> .docs-vendor/genie`; ~14k files in a dev checkout), and a re-rooted root `orca-plugin.json` does NOT fix that — do not reintroduce one, and do not add it to `scripts/version.ts`, the `version.yml` JSON_FILES list (still nine version files), or `release-guard.sh`. `plugins/genie` alone is symlink-free, ~132 files, ~1.3 MB, and holds the manifest at its root, so `.github/workflows/orca-plugin-ref.yml` force-pushes `git commit-tree HEAD:plugins/genie` (a parentless, history-free commit) to `refs/heads/orca-plugin` from main and `refs/heads/orca-plugin-dev` from dev, skipping when the tree hash already matches. Those refs are never merged back. The only repo-root Orca file is `orca-marketplace.json` — a source-only, versionless index pointing `automagik.genie` at ref `orca-plugin`, copied into no tarball. `scripts/orca-manifest-parity.test.ts` is the drift guard and also asserts `plugins/genie` stays symlink-free and inside the file cap. `genie setup --orchestration-mode orca` selects authority only; it never registers the plugin with Orca.
- **Lifecycle authority is an explicit mode, never inferred** — `standalone` is the default (including when `orchestration.mode` is absent); installing or opening Orca changes nothing. `genie setup --orchestration-mode orca` probes the shipped plugin payload and a compatible Orca runtime before atomically switching, after which Genie refuses local `genie.db` lifecycle reads/writes and roadmap writes/syncs/exports. Switching back with `--orchestration-mode standalone` imports no Orca state. There is no fallback database in either direction.
- **Two `genie.db` files, never cross-import** — per-repo `.genie/genie.db` (`genie-db.ts`, task/board/wish) and global `~/.genie/genie.db` (`global-db.ts`, omni queue + inbox) are independent databases with their own schemas and `user_version`. `global-db.ts` shares only `sqlite-open.ts` with the per-repo one — do not reach across for path constants.
- **Codex integration health is native state, not OTel** — the old Genie exporter at `127.0.0.1:14318` has no relay and is removed by an exact-match, backup-first migration. Preserve unrelated OTel settings and `disable_paste_burst`.
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,35 @@ genie doctor
start a compatible Orca runtime and repeat the Orca selection. Do not work around it with a private API, internal RPC,
terminal injection, or a local fallback.

### Installing the plugin in Orca

`genie setup --orchestration-mode orca` selects Orca as Genie's lifecycle authority. It does **not** register the Genie
plugin with Orca — that is a separate, Orca-side install. Orca accepts exactly two kinds of source:

- a **marketplace source**: a git repo whose *root* holds `orca-marketplace.json`;
- a **plugin source**: a git repo whose *root* holds `orca-plugin.json`, or a local folder containing `orca-plugin.json`.

**The genie repository root can never be the plugin tree.** Orca's loader rejects any install tree containing a symlink
("unsafe file path or symlink") and caps an install at 2000 files / 50 MB. This repo has `docs -> .docs-vendor/genie`,
runs to roughly 14,000 files in a dev checkout, and keeps its manifest nested at `plugins/genie/orca-plugin.json`, which
a git plugin source never looks at. So the plugin is published as a **tree-only git ref whose root *is*
`plugins/genie`** — symlink-free, ~132 files, ~1.3 MB:

| Route | What to give Orca |
|-------|-------------------|
| Marketplace source | `https://github.com/automagik-dev/genie.git`, ref `main` — the index; the plugin itself resolves to ref `orca-plugin` |
| Plugin git source | `https://github.com/automagik-dev/genie.git`, ref `orca-plugin` (stable) or `orca-plugin-dev` (pre-release) |
| Local folder | `~/.genie/plugins/genie` (what `genie install`/`genie update` ships) |

`.github/workflows/orca-plugin-ref.yml` republishes those refs: every push to `main` that touches `plugins/genie`
force-pushes a parentless commit carrying that subtree to `refs/heads/orca-plugin`, and every such push to `dev` does the
same to `refs/heads/orca-plugin-dev`. They are tree-only by design — no history, no shared ancestry with `main`, never
merged back. Orca pins the commit it fetched, so a republish cannot retroactively change an existing install.

The repo root carries only `orca-marketplace.json`, a source-only index no release tarball contains.
`scripts/orca-manifest-parity.test.ts` fails the build if the index drifts from the plugin's identity, or if
`plugins/genie` ever grows a symlink or crosses Orca's file cap.

### Install, update, rollback, and uninstall

Signed release tarballs include `plugins/genie/orca-plugin.json` and the compiled Orca entrypoint on every supported
Expand Down
16 changes: 16 additions & 0 deletions orca-marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Genie",
"owner": "automagik",
"plugins": [
{
"id": "automagik.genie",
"source": {
"kind": "git",
"url": "https://github.com/automagik-dev/genie.git",
"ref": "orca-plugin"
},
"description": "Genie workflows backed by Orca as the sole lifecycle authority.",
"categories": ["workflows"]
}
]
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@automagik/genie",

"version": "5.260830.9",
"version": "5.260830.12",
"description": "Collaborative terminal toolkit for human + AI workflows. NOTE: npm distribution discontinued 2026-05-09 — install via `curl -fsSL https://raw.githubusercontent.com/automagik-dev/genie/main/install.sh | bash` (cosign + SLSA verified). See https://automagik.dev/genie/release-process",

"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion plugins/genie/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genie",
"version": "5.260830.9",
"version": "5.260830.12",
"description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
"author": {
"name": "Namastex Labs"
Expand Down
2 changes: 1 addition & 1 deletion plugins/genie/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genie",
"version": "5.260830.9",
"version": "5.260830.12",
"description": "Plan, execute, review, and ship software with Genie workflows in Codex.",
"author": {
"name": "Namastex Labs",
Expand Down
2 changes: 1 addition & 1 deletion plugins/genie/.kimi-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genie",
"version": "5.260830.9",
"version": "5.260830.12",
"description": "Human-AI partnership for Kimi Code CLI. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /genie:work, validate with /genie:review, and ship as one team.",
"author": {
"name": "Namastex Labs",
Expand Down
4 changes: 2 additions & 2 deletions plugins/genie/orca-entrypoint.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion plugins/genie/orca-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"id": "genie",
"publisher": "automagik",
"name": "Genie",
"version": "5.260830.9",
"version": "5.260830.12",
"description": "Genie workflows backed by Orca as the sole lifecycle authority.",
"author": {
"name": "Namastex Labs",
Expand Down
13 changes: 9 additions & 4 deletions plugins/genie/orca-real-runtime-smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,11 @@ runRealSmoke(
return;
}

// A terminal that is not bound to a Run reads back `run: null`; the smoke
// must not dereference it — there is simply nothing to restore afterwards.
const prior = await runtime.execute({ operation: 'run-current' });
const priorRunId = String(object(object(prior.result).run).id);
const priorRun = object(prior.result).run;
const priorRunId = priorRun === null || priorRun === undefined ? null : String(object(priorRun).id);
const suffix = randomUUID().replaceAll('-', '').slice(0, 12);
let disposableTaskId: string | undefined;

Expand All @@ -94,12 +97,14 @@ runRealSmoke(
tasks.some((task) => object(task).id === disposableTaskId && object(task).status === 'completed'),
).toBe(true);
} finally {
await runtime.execute({ operation: 'run-use', id: priorRunId });
if (priorRunId !== null) await runtime.execute({ operation: 'run-use', id: priorRunId });
}

expect(disposableTaskId).toBeDefined();
const restored = await runtime.execute({ operation: 'run-current' });
expect(object(object(restored.result).run).id).toBe(priorRunId);
if (priorRunId !== null) {
const restored = await runtime.execute({ operation: 'run-current' });
expect(object(object(restored.result).run).id).toBe(priorRunId);
}
expect(await snapshotLocalLifecycle()).toEqual(before);
},
30_000,
Expand Down
2 changes: 1 addition & 1 deletion plugins/genie/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genie-plugin",
"version": "5.260830.9",
"version": "5.260830.12",
"private": true,
"description": "Runtime dependencies for genie bundled CLIs",
"license": "MIT",
Expand Down
56 changes: 56 additions & 0 deletions plugins/genie/references/orca-orchestration.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,62 @@ A new verb or field changes the trust boundary. Before implementation:
No fallback, cache, retry ledger, mirror, queue, lifecycle store, private host API, or background synchronization may be
added without a separately approved design.

## Installing the plugin in Orca

Selecting the authority (`genie setup --orchestration-mode orca`) and registering the plugin with Orca are two separate
acts. Genie never registers itself with Orca; the operator adds a source. Orca accepts exactly two:

1. a **marketplace source** — a git repo whose ROOT holds `orca-marketplace.json`;
2. a **plugin source** — a git repo whose ROOT holds `orca-plugin.json`, or a local folder containing `orca-plugin.json`.

### Why the repo root is not the plugin tree

Orca's loader imposes three constraints that this repository root cannot satisfy:

- **No symlinks.** Any symlink anywhere in the tree fails the whole install with "unsafe file path or symlink". `docs`
is a symlink into the `.docs-vendor` submodule.
- **2000 files / 50 MB.** A dev checkout is roughly 14,000 files.
- **The manifest must be at the ROOT.** Genie's manifest is nested at `plugins/genie/orca-plugin.json`, and a git plugin
source only ever looks at the root of the fetched tree.

Adding a second, re-rooted `orca-plugin.json` at the repo root does not help: it fixes only the third constraint and
leaves the first two fatal. `plugins/genie` on its own satisfies all three — symlink-free, ~132 files, ~1.3 MB, manifest
at its root — so that subtree is what gets published.

### The published refs

`.github/workflows/orca-plugin-ref.yml` mirrors `plugins/genie` into two refs of this same repository:

| Source branch | Published ref | Channel |
|---------------|---------------|---------|
| `main` | `refs/heads/orca-plugin` | stable — what `orca-marketplace.json` points at |
| `dev` | `refs/heads/orca-plugin-dev` | pre-release |

Each publish is a parentless commit created with `git commit-tree` over `HEAD:plugins/genie` and force-pushed. The refs
are **tree-only by design**: no history, no shared ancestry with `main` or `dev`, and never merged back. The workflow is
idempotent — it compares the branch's subtree hash against the published ref's tree and exits without pushing when they
match. Orca pins the commit it fetched, so a republish cannot retroactively change an existing install.

### Operator routes

- Marketplace source: `https://github.com/automagik-dev/genie.git` at ref `main` (the index lives at the repo root); the
entry it lists resolves the plugin from the same URL at ref `orca-plugin`.
- Plugin git source: `https://github.com/automagik-dev/genie.git` at ref `orca-plugin`, or `orca-plugin-dev` for the
pre-release channel. Never `main` or `dev` — those roots are not installable trees.
- Local folder: `~/.genie/plugins/genie`, the payload `genie install` / `genie update` ships. A contributor can point
Orca at the `plugins/genie` directory of a checkout for the same reason; the checkout ROOT is not a valid folder
source.

### Maintenance contract

`orca-marketplace.json` is source-only: `build-binary.sh` copies it into no tarball, so `release-payload-version.ts`
deliberately does not stamp or gate it. It carries no version field — it names an identity and a ref, both stable across
releases — so it is absent from `scripts/version.ts` and the `version.yml` bump list, and the repo root holds no
`orca-plugin.json` for either to stamp. `scripts/orca-manifest-parity.test.ts` is the drift guard: it fails when the
marketplace entry stops matching `publisher.id` or the description of `plugins/genie/orca-plugin.json`, when the source
ref stops being `orca-plugin`, when a root `orca-plugin.json` reappears, or when `plugins/genie` grows a symlink or
crosses Orca's 2000-file cap and stops being installable.

## Lifecycle and release maintenance

Install and update stage the complete payload, verify its inventory and version, preflight the selected authority, and
Expand Down
Loading
Loading