Skip to content

fix(init): keep the config keys pharn does not own across a re-run - #215

Merged
PrzemekGalarowicz merged 1 commit into
mainfrom
claude/wonderful-poitras-24c076
Sep 24, 2026
Merged

PrzemekGalarowicz merged 1 commit into
mainfrom
claude/wonderful-poitras-24c076

Conversation

@PrzemekGalarowicz

Copy link
Copy Markdown
Contributor

Problem

Upstream pharn-oss 6.15–6.20 reads top-level pharn.config.json keys that users add by hand, following the pharn-oss README's "Per-test results" section:

  • testResults, read by /pharn-test and /pharn-verify's acceptance-criteria check. Without it, /pharn-loop stops with blocked: no-test-runner.
  • ship.requireAttestation, read by /pharn-ship.

add, update and remove keep unknown keys, because they spread the loaded config. init does not: steps/install-archetype.ts builds a fresh config object, and the only thing carried over from the replaced config was source: 'manual' capabilities. So a re-run pharn init dropped both keys.

Fix

  • What is carried: init now copies every top-level key the CLI does not declare in PharnConfig. It is appended after pharn's own keys, so the committed file's diff stays small.
  • Why every key, not only these two (reasoning in the comment on userOwnedConfigEntries):
    • The other writers already keep unknown keys; a short list would make init the one command where a key survives only if this CLI release knows it.
    • Released CLIs install pharn-oss's latest main, so a fixed list would drop the next upstream key in every deployed CLI.
    • pharn's own keys are the closed, known set, so they are the side to list.
  • Exhaustive by construction: the owned-key list is checked with satisfies Record<keyof PharnConfig, true>. A field declared on PharnConfig but missing from the list, or listed but not declared, fails the typecheck. I confirmed both directions.
  • Tolerant read: the old file is read raw, inside init's lock, just before the write, and any read or parse failure carries nothing. It deliberately does not go through readPharnConfig: that returns null for a config with no modules array, which is the case where every other command tells the user to run pharn init.
  • Plain data: keys are matched with a Set and rebuilt with Object.fromEntries. A key named constructor, toString or __proto__ round-trips as an ordinary key.
  • Never carried: skillsVersion, layout, models, seam, pendingSkillsVersion, frozenCapabilities, the legacy fields, and every other key pharn owns.

Tests

8 new cases in tests/init-archetype.test.ts. The first 5 below failed before the fix; the last 3 already passed and pin the fallback.

  • testResults and ship are kept verbatim, and nothing else is added.
  • A config with every pharn-owned key hand-edited comes out as a fresh install plus testResults.
  • Keys are still kept when the config has no modules array.
  • Keys are still kept when the config has an invalid seam block.
  • Prototype-named keys stay plain data.
  • A config that is not JSON, or is a JSON array or plain value, carries nothing and the install still succeeds.

npm run check passes (1480 tests), and so does npm run build, both run on the branch after rebasing onto main.

Docs

  • docs/reference/pharn-config.md: new "Keys pharn does not own" section. It also fixes an out-of-date note that said a re-run init loses manual capabilities.
  • docs/commands/init.md, docs/troubleshooting.md: short updates to match.
  • CHANGELOG.md: an entry under Unreleased / Fixed.

🤖 Generated with Claude Code

Upstream pharn-oss 6.15+ reads top-level pharn.config.json keys that users
add by hand: `testResults` (without it /pharn-loop stops `blocked:
no-test-runner`) and `ship.requireAttestation`. add/update/remove keep
unknown keys through the loaded config's spread, but init rebuilt the
config from its own fields and dropped them.

init now copies every top-level key the CLI does not declare in
PharnConfig across from the config it replaces. It reads the old file raw
and tolerantly, just before the write: any read or parse failure carries
nothing. The owned-key list is exhaustive by
`satisfies Record<keyof PharnConfig, true>`, so a pharn-owned field can
never be carried over stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 24, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 8e99a238-e9f0-437d-98ef-07357d58aa71


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@PrzemekGalarowicz
PrzemekGalarowicz merged commit f9b1bbb into main Sep 24, 2026
12 checks passed
@PrzemekGalarowicz
PrzemekGalarowicz deleted the claude/wonderful-poitras-24c076 branch September 24, 2026 19:09
PrzemekGalarowicz pushed a commit that referenced this pull request Sep 24, 2026
Resolve the overlap with #215 (init keeps the config keys pharn does not
own): both helpers are kept, `keptRecords` and `readCarriedEntries`. The
three doc passages and the CHANGELOG now describe both carry-overs.

Two of #215's comments are updated. One cited the removed
carriedManualCapabilities. The other said a key edited while init's prompt
is open is carried; init now refuses first when the config changed, so the
key is still not lost. Adds one test: kept entries and user-owned keys
survive the same re-run. Regress and verify re-run on the merged tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0199owRmYfskqYQVQrVP679o
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.

1 participant