# Project seeds

Each direct child directory is bundled into the Agent Codex image as a project
seed. At container startup, a seed is copied to `PROJECTS_ROOT_DIR` (default:
`/data/projects`) only when the destination directory does not already exist.

After copying a new project into a temporary directory, startup runs
`git init --initial-branch=main` there before atomically moving it into place.
Files are not staged or committed. If Git initialization fails, the temporary
directory is removed and startup stops. Existing projects are skipped even when
they have no Git repository. Global seeds do not receive Git initialization.

The reserved `__global__` directory seeds global Codex content instead of a
project:

```text
__global__/
  AGENTS.md
  skills/
    <skill-name>/
      SKILL.md
    lark-cli/
      lark-shared/
        SKILL.md
      lark-doc/
        SKILL.md
```

`AGENTS.md` and `skills` are optional. Each direct child of `skills` can be a
complete skill or a group containing skills in subdirectories. Each child tree
must contain at least one regular `SKILL.md`. Global content is copied to
`$CODEX_HOME/AGENTS.md` and `$CODEX_HOME/skills/<skill-or-group-name>`, preserving
the nested directory structure. Each top-level skill or group is seeded atomically;
if that destination already exists, the entire tree is skipped, including any
new skills added to the image under the same group.

Existing projects, global skills, and global `AGENTS.md` are never merged or
updated. Removing a seed from the image does not remove its persistent copy,
while deleting the persistent copy causes it to be seeded again on the next
startup.

Do not include secrets, symlinks, or special files in a seed. When overriding
`PROJECTS_ROOT_DIR`, keep the volume mount and the Agent Sidecar
`projectsRootDir` setting aligned with it.

The `ticket-manager` project seed contains `triage` and `workspace` skills
under `.agents/skills`. Use the seeded project as the Agent working directory
(by default `/data/projects/ticket-manager`). Workspace task advisor uses the
image's `ticket-manager-cli` to create advice. The skill passes the fixed
`${DS_PROXY_URL}/devops/prod/ticket-manager` route as `--base-url`. Sidecar supplies
authentication and must allow advice creation on that route.
Lark chat reads use the global skills and Auth Proxy configuration described
below. App Secret and Tenant Access Tokens stay inside Sidecar.

The image builds `ticket-manager-cli` from `apps/cli/ticket-manager` and installs
it on PATH. Existing PVCs need the reviewed workspace skill and global AGENTS.md
updates as well as the new image; startup does not overwrite existing skills.

## Internal API access

Bundled API skills require `DS_PROXY_URL` in the process environment and send
requests through the Sidecar route prefixes. They do not load credential files
or fall back to direct upstreams. `DEVOPS_ENV` defaults to `prod`. The global
`__global__/AGENTS.md` documents route conventions and unavailable capabilities.

Validate the client transport from the repository root without internal credentials:

```bash
PYTHONDONTWRITEBYTECODE=1 python3 infra/images/agent-codex/proxy-skills_test.py
```

This test starts a local loopback HTTP server; it does not contact internal APIs.

## Lark Auth Proxy

The image builds `larksuite/cli v1.0.88` (source commit
`2829ecd18846d8390dfac558125f602b07232206`) with `authsidecar` enabled. The ordinary
npm binary is not a replacement. Public API metadata is fetched at image build
time and embedded; runtime metadata refresh and update notifiers are disabled.

Runtime configuration must supply:

```text
LARKSUITE_CLI_AUTH_PROXY=http://127.0.0.1:16384
LARKSUITE_CLI_PROXY_KEY=<runtime-injected proxy signing key>
LARKSUITE_CLI_APP_ID=<same app ID as the Sidecar token source>
LARKSUITE_CLI_BRAND=feishu
LARKSUITE_CLI_DEFAULT_AS=bot
LARKSUITE_CLI_STRICT_MODE=bot
```

Use `lark` as the brand for `open.larksuite.com`. The image already sets the last
two variables to `bot`. The signing key must match the Sidecar middleware's
`keyFrom` value byte-for-byte: a random hex string is used verbatim, not decoded.
Never bake its value into the image, project seed, repository or logs. App Secret
and real Tenant Access Tokens are resolved only inside Sidecar.

Unlike other bundled API clients, Lark does not use `DS_PROXY_URL`. It sends
signed requests to the dedicated TCP listener and uses `--as bot`. Follow
`__global__/skills/lark-cli/lark-shared/SKILL.md` for configuration
checks, supported operations and failure handling. The Sidecar example permits
only bot info; each additional API method/path needs an operation-policy entry.
Some shortcuts may probe user identity before their bot request; this probe is
rejected by the bot-only proxy and does not grant user access.

The runtime copy consists of these global skills: `lark-shared`, `lark-base`,
`lark-doc`, `lark-im`, `lark-openapi-explorer`, `lark-sheets`, and `lark-wiki`.
They are maintained here as the runtime source of truth. When updating them
from upstream, preserve the Sidecar Auth Proxy instructions.

### Existing persistent Agents

A new image does not update existing project or global seed destinations.
On startup, the Lark group is seeded into `$CODEX_HOME/skills/lark-cli/` when
that directory does not exist.
When deploying this change, synchronize any reviewed updates to existing global
Lark skills and `$CODEX_HOME/AGENTS.md`, preserving unrelated local edits.
After verifying the global copies, remove the old project-level Lark skills
from `$PROJECTS_ROOT_DIR/ticket-manager/.agents/skills/lark-cli/`, preserving
any local changes that still need to be migrated. Start a new task to load the
updated instructions. This change does not automatically overwrite or remove
persistent content.

### Validation

```bash
PYTHONDONTWRITEBYTECODE=1 python3 infra/images/agent-codex/lark-proxy-skills_test.py
```

The service's real-client integration test uses a locally built `authsidecar`
binary and synthetic credentials only:

```bash
cd services/agent-sidecar
LARK_CLI_TEST_BINARY=/absolute/path/to/lark-cli GOWORK=off \
  go test ./internal/services/apiproxy -run TestLarkCLIIntegration -count=1
```
