Files
bot-agents/.agents/memory/bot-agents-migration-decisions.md
T
Danny Kim 32806120b3 Group the sources into aliased modules and lint with the published plugins (#23)
Groups the flat `src/` into cohesive modules and turns on the published lint plugins. No behavior change.

## Module layout

Each module is a direct `src/` subfolder with an `index.ts` barrel and a `#` alias in `package.json` `imports` (layer order, top first):

| Alias | Files |
| --- | --- |
| `#prepare` | `index.ts` (the prepare phase entry point) |
| `#run` | `index.ts` (the run phase entry point) |
| `#post` | `index.ts` (the post phase entry point), `post.ts`, `post.test.ts` |
| `#agents` | `auth.ts`, `opencode.ts` (Codex / Claude Code sign-in, OpenCode args and output) |
| `#processes` | `child.ts`, `isolation.ts` |
| `#runtime` | `runtime.ts`, `credentials.ts` |
| `#profiles` | `profiles.ts` |
| `#common` | `errors.ts`, `json.ts` |
| `#testing` | `index.ts` (unchanged) |

Every top-level entry of `src/` is an aliased module folder. Tests stay next to their code as `*.test.ts`. Each phase's `index.ts` is its entry point, and `action.yml` now runs `node src/<phase>/index.ts`. The post phase's logic moved to `src/post/post.ts` so its test can import it; `index.ts` keeps the env reading and the `import.meta.main` call. `run`'s prompt URL is now `../../prompt.md`. `scripts/protect-source.sh` now copies `src/` recursively (it used to copy `src/*.ts` only), and the workflow test checks that module files get staged too.

## Lint plugins

- New devDependencies: `eslint-plugin-explicit-types@^0.6.1` and `eslint-plugin-import-structure@^0.10.2`, both in `jsPlugins`. The lockfile comes from a real `npm install`.
- Enabled configs: `importStructure.configs.modules`, `explicitTypes.configs.concrete`, `explicitTypes.configs.absence`.
- **explicit-types `naming` is off** (the other app repos turn it on). Its `upper-snake-string-unions` and `no-upper-snake-object-keys` rules fire on wire tokens this repo doesn't choose: profile types `"claude" | "codex" | "opencode"` from `profiles.json`, failure reasons posted to Gitea, Gitea's `"new" | "old"` side, and environment variable names such as `PATH`. Satisfying them would change behavior or need opt-outs. This is logged as an open question for the user.

Lint fixes, all type-level:
- `Pick<BotProfile, "type" | "model">` became a named `ProfileIdentity` interface that `BotProfile`, `ProfileFailure`, and `RuntimeProfile` extend. `RuntimeOutcome` is now `RuntimeSuccess | RuntimeFailure`.
- Inline object types became named interfaces.
- `Record<string, string>` env maps became `NodeJS.ProcessEnv`. `JsonObject` and the verdict table became `NodeJS.Dict<…>`.
- `T | undefined` locals and returns became `T | null` with matching checks. `typeof fetch` became a `Fetcher` type.
- Claude's result fields are held in camelCase (`isError`, `apiErrorStatus`) after parsing, because `id-match` rejects snake_case interface members.
- Duplicate import sources are merged (`import-structure/no-duplicate-import-source`).

## Verification

- `vp check`: clean.
- `vp test`: 61 passed and 5 skipped as a normal user. All 66 passed as root in `node:24-bookworm`.
- Each phase (`node src/<phase>/index.ts`) run from a `protect-source.sh` copy loads its full alias graph under Node.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #23
Reviewed-by: bot <noreply@capsulizers.com>
Co-authored-by: Danny Kim <temeddix@gmail.com>
Co-committed-by: Danny Kim <temeddix@gmail.com>
2026-10-18 15:00:16 +00:00

2.2 KiB


description: bot-agents migration decisions: live @main action for six repos, JSON profiles, Node 24.2+, lints with eslint-plugin-import-structure and explicit-types (concrete, absence)

Decisions for commons/bot-agents (Capsulizers Gitea), from the Deno to Node + Vite+ migration (PR #20, node-migration, 2026-10-15/16):

  • Live action. Six consumer repos use commons/bot-agents@main, so a merge switches all of them at once. It changes only through a PR and a careful review. Suggested before merging a risky change: tag the current main (e.g. deno-final) as a rollback target and canary the branch from one consumer (uses: .../commons/bot-agents@<branch>) on a throwaway PR.
  • Profiles are JSON. profiles.yml became profiles.json with the same content (encrypted v1: values unchanged), read with JSON.parse. That removed the only runtime dependency (@std/yaml); keep it free of runtime dependencies (@eslint/core and the other devDependencies serve only the tooling).
  • Node 24.2+, not 22: src/post/index.ts uses import.meta.main. There is no engines (user decision 2026-10-18): the node:24-bookworm job image decides the version (bot-agents-runtime-no-build).
  • Published lint plugins (2026-10-18, after they reached npm): eslint-plugin-import-structure (modules config) and eslint-plugin-explicit-types (concrete and absence configs) are devDependencies, enabled in vite.config.ts. explicit-types' naming config is left off: its upper-snake-string-unions and no-upper-snake-object-keys rules fire on wire tokens this repo does not name (profile types such as "claude", failure reasons posted to Gitea, environment variable names), which cannot change without changing behavior. Open question for the user. eslint-plugin-lit-robust does not apply (no Lit).
  • Stricter input checks were accepted in the migration: Gitea /user and comment responses, the runtime manifest, and the outcome's profile type are validated; some type errors became TypeError with the same messages.
  • Merging is the user's call; the bot account's approval is required first (git-pr-workflow).