32806120b3
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>
2.2 KiB
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 currentmain(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.ymlbecameprofiles.jsonwith the same content (encryptedv1:values unchanged), read withJSON.parse. That removed the only runtime dependency (@std/yaml); keep it free of runtime dependencies (@eslint/coreand the other devDependencies serve only the tooling). - Node 24.2+, not 22:
src/post/index.tsusesimport.meta.main. There is noengines(user decision 2026-10-18): thenode:24-bookwormjob image decides the version (bot-agents-runtime-no-build). - Published lint plugins (2026-10-18, after they reached npm):
eslint-plugin-import-structure(modulesconfig) andeslint-plugin-explicit-types(concreteandabsenceconfigs) are devDependencies, enabled invite.config.ts. explicit-types'namingconfig is left off: itsupper-snake-string-unionsandno-upper-snake-object-keysrules 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-robustdoes not apply (no Lit). - Stricter input checks were accepted in the migration: Gitea
/userand comment responses, the runtime manifest, and the outcome's profile type are validated; some type errors becameTypeErrorwith the same messages. - Merging is the user's call; the
botaccount's approval is required first (git-pr-workflow).