Replace NodeJS dict aliases with maps and named field reads #24
Reference in New Issue
Block a user
Delete Branch "honest-dict-types"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
PR #23 swapped
Record<string, ...>forNodeJS.Dict<unknown>andNodeJS.ProcessEnv. Both are index-signature aliases, so they slip pastno-index-signaturesandno-utility-typeswithout following the rules' intent. This PR swaps them for honest types:sanitizedEnvironment,agentEnvironment, the runner's process env, prompt template values, test helpers) are nowMap<string, string>/ReadonlyMap<string, string>. They becomeObject.fromEntries(...)only at thespawn/spawnSyncenvboundary, which adds no index-signature type of our own.VERDICTSlookup table is now aMap.JsonObjectis gone.isObject/isMappingnow narrow toobject, and a newfield(value, name): unknownreads each known field by name. The parsers fill their declared interfaces (BotProfile,RuntimeOutcome,Anchor,ClaudeResult) from those reads, so nothing gets indexed through a dict type.tests/workflow.test.ts: parsed YAML mappings, whose keys are dynamic, becomeMap<string, unknown>. ItsJSON.stringifychecks now stringifyObject.fromEntries(...), because aMapwould otherwise stringify to{}.A focused
src/common/json.test.tscoversfield.Lint: blacklist policy
vite.config.tsturns on every Oxlint category except nursery (correctness, suspicious, pedantic, perf, style, restriction). It enables each plugin through itsallconfig and setsoverrides: projectOverrides.scripts/project-lint.tsholds the repo rules, option tweaks, and offs, each with a one-line reason. It also holdsprojectOverrides: the Node code entry first, then the per-file exceptions, each with a reason.one-var"never",no-use-before-define{ functions: false },func-styledeclarations with arrows allowed,id-lengthsingle capital type parameters. Size limits are set to the smallest passing values:complexity26,max-depth6,max-lines-per-function124,max-params4,max-statements32,import/max-dependencies13,unicorn/max-nested-calls4,max-lines2048.no-await-in-loop, because sequential awaits are intentional.consistent-return,typescript/switch-exhaustiveness-check, anddefault-case, because TypeScript covers them; a forceddefault:would hide missing cases.typescript/promise-function-asyncwithrequire-await/return-await, using theconst x = await g(); return x;pattern.init-declarations, usinglet x: T | null = null.unicorn/no-array-reduce. The runtime profile setup,runProfiles, and the Codex login poll are back to main's plain loops.unicorn/custom-error-definition.GiteaRequestErrorandAgentAuthErrorsetname.explicit-types/upper-snake-string-unions.CLAUDE/CODEX/OPENCODE, including inprofiles.jsonand footers.SIGN_IN_FAILED/USAGE_EXHAUSTED/FAILED.toMessage()keeps the posted words.sideisNEW/OLD, asprompt.mdnow tells the agent. It maps to Gitea'snew_position/old_position.switchfunctions convert at each boundary:skillsAgent()for the skills installer,tokenVariable()for credential env names, anddefaultModel().prefer-ascii,no-nan, and the hugoalh ports from import-structure:no-duplicate-typeofs,no-empty-yield,no-unsafe-number,no-useless-continue,no-duplicate-interfaces,no-duplicate-types,no-useless-type,no-modifier-private.no-enumcomes fromexplicit-types/no-enumin itsallconfig, andno-duplicate-awaits/no-duplicate-voidsare left out becauseunicorn/no-unnecessary-awaitandtypescript/no-meaningless-void-operatorcover them.scripts/project-lint.ts.src/processes/child.tsturns offpromise-function-async.exitCodemust return the promise it marks handled, or an async wrapper would reject unhandled before callers await it.src/post/andsrc/profiles/(code and tests) turn offprefer-ascii, because posted text uses emoji verdict marks and a middle dot.eslint-plugin-explicit-typesis updated to 0.7.0..agents/memorynotes record these decisions. The newenum-values-upper-snake.mdcovers UPPER_SNAKE values.Batch 4 decisions (2026-10-20)
vite.config.tsgets the apps'fmtblock (printWidth: 80,quoteProps: "preserve",embeddedLanguageFormatting: "off"), andvp fmtrewraps the code.max-lines-per-functionrises to 124, sincepostRuntimeis longer at the narrower width.unicorn/import-styleis back to its default:import nodePath from "node:path"plusnodePath.join(...).no-template-curly-in-stringis on repo-wide. Onlytests/workflow.test.ts, which checks literal Gitea${{ }}expressions, is exempt inprojectOverrides.unicorn/number-literal-case(Oxfmt lowercases hex digits back) andtypescript/consistent-return(misfires on complete switches, likeconsistent-return).prefer-object-spread,unicorn/prefer-spread,unicorn/no-useless-spread,typescript/no-misused-spread,no-plusplus,unicorn/no-array-method-this-argument,typescript/method-signature-style,typescript/unified-signatures.vp checkandvp testpass. No Rust here, so the serde UPPER_SNAKE item does not apply.🤖 Generated with Claude Code
@bot
✅ Approved
Map-based environment and JSON field access preserve the action behavior while removing the prohibited dictionary aliases.
Strengths
Typed boundaries
Issues
None
No Critical, Important, or Minor findings.
Recommendations
None
No follow-up recommendations.
Declined to judge
None
No behavior was set aside.
Checks
Passed
Check / check (pull_request)npm exec -- vp checknpm exec -- vp test— 64 passed, 5 skipped@bot
✅ Approved
Strengths
Typed boundaries
Issues
None
No Critical, Important, or Minor findings.
Recommendations
None
No follow-up recommendations.
Declined to judge
None
No behavior was set aside.
Checks
Passed
npm exec -- vp checknpm exec -- vp test— 64 passed, 5 skippedd09806d@bot
✅ Approved
Strengths
Typed boundaries
Issues
None
No Critical, Important, or Minor findings.
Checks
Passed
npm exec -- vp checknpm exec -- vp test— 64 passed, 5 skippeddf66beaDeclined to judge
None
No behavior was set aside.
@bot
✅ Approved
Strengths
Safe boundaries
Issues
None
No Critical, Important, or Minor findings.
Checks
Passed
Check / check (pull_request)onf2de920npx vp checknpx vp test— 64 passed, 5 skippedDeclined to judge
None
No behavior was set aside.
@bot
✅ Approved
Strengths
Safe typed boundaries
Issues
None
No Critical, Important, or Minor findings.
Checks
Passed
Check / check (pull_request)on8b9aedfnpx vp checknpx vp test— 64 passed, 5 skippedDeclined to judge
None
No behavior was set aside.
@bot review
✅ Approved
Summary: Map-based dynamic data and named JSON-field reads are applied consistently; required checks pass.
Strengths
Issues
Recommendations
Assessment
Strengths
Issues
Recommendations
Declined to judge
Assessment
npm exec -- vp checkandnpm exec -- vp testpass ond0cf8b93cba38c96241a8d26cf80435eef3abd6b.@bot review
✅ Approved
Map-based dynamic data and named JSON reads meet the PR requirements; checks pass.
Strengths
Safe boundaries
Issues
None
Recommendations
None
Assessment
Ready to merge
npx vp checkpassed.npx vp testpassed: 64 tests, 5 skipped.41a9c62.Declined to judge
None
@bot
✅ Approved
Map-based dynamic values and named JSON reads meet the PR requirements; current-head checks pass.
Strengths
Safe boundaries
Issues
None
Recommendations
None
Assessment
Ready to merge
npm exec -- vp checkpassed.npm exec -- vp testpassed: 65 passed, 5 skipped.Check / check (pull_request)passed onca619888b2af14a0ee8593bfe9f23c0ed86b5c30.Declined to judge
None
@bot
✅ Approved
Map-based dynamic data, named JSON reads, and documented lint policy updates meet the PR requirements.
Strengths
Issues
Recommendations
Assessment
Typed boundaries
Policy alignment
Issues
Recommendations
Checks
npm exec -- vp checkpassed.npm exec -- vp testpassed: 65 tests; 5 skipped.a429e6f.Declined to judge
Assessment
@bot
✅ Approved
Map-based dynamic values, named JSON reads, and the lint-policy update meet the PR requirements; checks pass.
Strengths
Issues
Recommendations
Assessment
Typed boundaries
Issues
Recommendations
Checks
Check / check (pull_request)passed on6503d3b.npx vp checkpassed.npx vp testpassed: 65 tests; 5 skipped.Declined to judge
Assessment