Skip to content

feat(config): add HARPER_CONFIG as the recommended way to set config from the environment - #1238

Merged
kriszyp merged 2 commits into
mainfrom
feat/harper-config-env-var
Jun 11, 2026
Merged

feat(config): add HARPER_CONFIG as the recommended way to set config from the environment#1238
kriszyp merged 2 commits into
mainfrom
feat/harper-config-env-var

Conversation

@heskew

@heskew heskew commented Jun 10, 2026

Copy link
Copy Markdown
Member

Summary

Adds HARPER_CONFIG as the recommended environment variable for setting Harper configuration. It takes a JSON config object and applies it as a merge on top of the existing configuration: it sets exactly the keys it names (at any depth), reasserts them on every boot, and yields only to HARPER_SET_CONFIG.

Closes #1214 (sub-issue of #1097). Builds on the $union directive from #1213.

Precedence (later wins)

HARPER_DEFAULT_CONFIG  <  config file / user edits  <  HARPER_CONFIG  <  HARPER_SET_CONFIG
Variable Use it when Merge behavior
HARPER_CONFIG (recommended) Set configuration — the normal default Merge; only the named keys change; reasserts every boot
HARPER_DEFAULT_CONFIG Provide overridable defaults Merge, yields to file/user/individual vars
HARPER_SET_CONFIG Force + lock against drift Override (existing force semantics)

Behavior (the four decisions, confirmed with @heskew)

  1. Drift: while the var names a key, the env wins — a manual config-file edit to that key is overwritten on the next restart (12-factor intuition). This is what distinguishes it from DEFAULT.
  2. Deletion: dropping a key from HARPER_CONFIG restores the pre-override original (or deletes the key if HARPER_CONFIG introduced it), via the existing snapshot machinery — symmetric with DEFAULT/SET.
  3. Individual HARPER_* env vars win over HARPER_CONFIG for the keys they name (filterArgsAgainstRuntimeConfig stays SET-only — unchanged).
  4. DEFAULT stays as-is — three vars, three niches (defaults / recommended-merge / force).

Implementation

  • New 'HARPER_CONFIG' ConfigSource + snapshot slot; applied via applyConfigLayer with respectSources: ['HARPER_SET_CONFIG'] + storeOriginals, same at install and runtime.
  • Added to applyRuntimeEnvConfig, composeConfigFromEnv (clone/pre-install), the configUtils.js early-return guard, and the restore-original deletion/cleanup paths.
  • $union flows through HARPER_CONFIG for free via the shared apply path.

Where to look

  • Cleanup-before-apply ordering (applyRuntimeEnvConfig) — this PR moves all removed-var cleanups ahead of the applies. A domain review caught that the original apply-then-cleanup order gave a one-boot window where removing HARPER_SET_CONFIG (while HARPER_CONFIG still named the path) dropped the value to the file original for one boot before self-healing. Reordering makes HARPER_CONFIG reclaim the path the same boot. Behavior is unchanged for present-var and DEFAULT/SET-only cases (a removed var's own apply is a no-op). New regression test: "reclaims a path the SAME boot HARPER_SET_CONFIG is removed".
  • Known-benign quirk: when HARPER_CONFIG and HARPER_SET_CONFIG name the same path in one boot, HARPER_CONFIG's snapshot records the value it set before SET overrode it, while sources[path] ends up HARPER_SET_CONFIG. No data loss — deletion paths gate on current source ownership (verified) — but the snapshot is momentarily not authoritative for that path. Left as-is to avoid complicating buildSnapshot; flagging for the reviewer.

Testing

13 new unit tests in unitTests/config/harperConfigEnvVars-config.test.js: merge/sibling preservation, source+original tracking, $union, all four precedence relationships (file, user-edit reassert, DEFAULT, SET incl. not stealing SET-owned paths), the SET→CONFIG same-boot hand-back, drop-key restore, var-removal cleanup, introduced-key deletion, SET-only arg filtering, and four-layer composeConfigFromEnv ordering. Full unitTests/config/** suite green (174 passing); build + lint + format clean.

Review note

Cross-model review was domain-only this round: Codex hit its weekly session limit and the local Gemini (agy) leg hangs in this environment. The Harper-domain pass (which found the ordering issue above) ran; the GitHub Gemini Code Assist + Claude review bots will provide the outside-model legs on this PR.


🤖 Generated with Claude Code (agent: Claude Opus 4.8)

…nv var

HARPER_CONFIG takes a JSON config object and applies it as a merge on top
of the existing configuration: it sets exactly the keys it names (at any
depth), reasserts them on every boot (winning over the config file, user
edits, and HARPER_DEFAULT_CONFIG), and yields only to HARPER_SET_CONFIG.
Precedence ladder (later wins):

  HARPER_DEFAULT_CONFIG < config file / user edits < HARPER_CONFIG < HARPER_SET_CONFIG

Omitted keys are restored to their pre-override originals via the existing
snapshot machinery (same as DEFAULT/SET). Individual HARPER_* env vars
still win over HARPER_CONFIG (arg filtering remains SET-only). The $union
array directive works through HARPER_CONFIG via the shared apply path.

Closes #1214 (sub-issue of #1097)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@heskew
heskew requested review from DavidCockerill and kriszyp June 10, 2026 22:12
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@claude

claude Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Reviewed; no blockers found.

@heskew
heskew marked this pull request as ready for review June 10, 2026 22:30
@heskew

heskew commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

Cross-model review status for this PR: the outside-model legs were rate-limited this round —

  • Codex: weekly session limit reached (couldn't run).
  • Gemini (both the local agy leg and the GitHub Gemini Code Assist bot): agy hangs in this environment, and the bot reported "reached your daily quota limit."
  • Claude PR Review bot: ran clean — "no blockers found."
  • Harper-domain review pass (Opus, local): ran and caught one significant issue — a one-boot misconfiguration window when HARPER_SET_CONFIG is removed while HARPER_CONFIG still names the path — now fixed (cleanup-before-apply reorder) with a regression test.

So a genuine non-Claude review did not land this round. Happy to re-run a Gemini/Codex pass once the limits reset (Gemini ~24h) before merge if you'd like that belt-and-suspenders — otherwise this is resting on the domain pass + clean Claude-bot pass + your human review.


🤖 Posted by Claude on Nathan's behalf

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

Comment thread config/configUtils.js Outdated
…or apply

The applyRuntimeEnvVarConfig wrapper short-circuited whenever no config env
vars were set, returning before applyRuntimeEnvConfig could run its
snapshot-based cleanup. So removing all three vars after a prior boot left
the last-applied values persisted and the snapshot uncleared (pre-existing
for DEFAULT/SET; HARPER_CONFIG makes removal routine).

Add hasPersistedEnvConfigState(rootPath) and only short-circuit when no env
vars AND no prior state; otherwise delegate so originals are restored and
the snapshot cleared. Resolve rootPath before the guard (warn only when
there is config to apply).

Raised by Codex review on PR #1238.

Refs #1214

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@heskew

heskew commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Re: the Codex review — nice catch on the state-unaware early return in applyRuntimeEnvVarConfig. It was a real, pre-existing gap (DEFAULT/SET had it too), just made routine by HARPER_CONFIG being the recommended var people add and remove.

Fixed in 826a3a6: the wrapper now skips only when there are no config env vars and hasPersistedEnvConfigState(rootPath) is false; otherwise it delegates so applyRuntimeEnvConfig restores originals and clears the snapshot. Covered by three new tests (no-vars-no-state → skip; no-vars-but-state → cleanup runs; a direct predicate test). Inline thread resolved. ✅


🤖 Posted by Claude on Nathan's behalf

@kriszyp
kriszyp merged commit ecbfd78 into main Jun 11, 2026
83 of 84 checks passed
@kriszyp
kriszyp deleted the feat/harper-config-env-var branch June 11, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add HARPER_CONFIG as the recommended mergeable config env var

2 participants