Skip to content

fix(nix): unbreak t3code build and shell-test on main - #2363

Merged
shunkakinoki merged 11 commits into
mainfrom
claude/fix-t3code-pnpm-hash
Aug 12, 2026
Merged

fix(nix): unbreak t3code build and shell-test on main#2363
shunkakinoki merged 11 commits into
mainfrom
claude/fix-t3code-pnpm-hash

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Three fixes for red builds on main.

1. t3code build failure (overlays/default.nix)

make build fails on every x86_64-linux host:

error: hash mismatch in fixed-output derivation 't3code-pnpm-deps.drv':
         specified: sha256-i/K5bj7CS7PGIX5hfayxAJ7ngNib92w3SDKGXTVWccA=
            got:    sha256-sV9aynsvHXopeCkZ0Myjj+FrkySXAooh7SVSNsuL56c=

Upstream pins a single pnpmDeps hash for t3code 0.0.33, but fetchPnpmDeps resolves platform-specific optional packages, so it only reproduces on the system upstream generated it from. Repinned for x86_64-linux only — other systems keep the upstream hash and cannot regress. Follows the existing grok/bernstein workaround pattern.

t3code-desktop needed separate handling: it is a symlinkJoin over t3code's desktop output with no pnpmDeps of its own, so overriding t3code alone left it pulling the broken build. Its paths are repointed at the repinned derivation.

Verified by eval against the matic config — t3code.pnpmDeps.outputHash is the corrected hash, and t3code-desktop.paths resolves to a new store path rather than the original.

Drop this override once upstream repins. A nix flake update llm-agents will not help: their last t3code commit predates our pin.

2. shell-test failure (spec/llm_update_spec.sh)

#2358 merged with shell-test red — two specs I added used yq with jq-syntax filters. yq is the kislyuk Python build locally and the mikefarah Go build in CI, and no filter is valid in both:

Error: 1:169: lexer: invalid input text "index($r.value))..."
2071 examples, 2 failures

Rewritten to parse the generated config with python3 regexes — no YAML library, no dialect assumption. python3 is already used by three existing specs; yq was used by none before mine. Also adds a guard that every Pi fallback model resolves against config/pi/models.json, since an unresolvable entry silently cools the whole chain down at runtime.

3. Dead placeholder (config/omp/config.tpl.yml)

#2360 removed gpt-codex-spark from the canonical models.json, but the OMP default fallback chain still referenced __GPT_CODEX_SPARK__, so the next llm-update.sh run rendered the literal placeholder into the config. Repointed to __GPT_CODEX__.

Testing

  • Full local suite: 2072 examples, 0 failures (CI on main: 2071 examples, 2 failures).
  • nix fmt clean.
  • t3code overrides verified by eval; the x86_64-linux build itself cannot be verified from an aarch64-darwin host, so the hash is the one reported by the failing host.

Summary by cubic

Fixes red builds on main by repinning t3code pnpmDeps on x86_64-linux, repointing t3code-desktop, replacing a removed OMP model in the default chain, and making OMP shell tests independent of yq dialects. Before: x86_64-linux builds failed on a fixed-output hash mismatch, CI shell tests errored on yq, and the template rendered a dead placeholder. After: x86_64-linux builds pass (other platforms unchanged), tests parse with python3, and configs reference __GPT_CODEX__/gpt-5.3-codex.

  • Nix overlay: conditionally overrides t3code pnpmDeps.outputHash for x86_64-linux and repoints t3code-desktop.paths to the repinned desktop output; other systems keep the upstream hash. Remove this override once upstream repins (a nix flake update llm-agents will not fix this).
  • Tests: rewrites spec/llm_update_spec.sh to parse OMP config with python3 regexes instead of yq, avoiding kislyuk vs mikefarah dialect conflicts; adds a guard that every Pi fallback model resolves against config/pi/models.json.
  • Config: replaces __GPT_CODEX_SPARK__/gpt-5.3-codex-spark with __GPT_CODEX__/gpt-5.3-codex in the OMP default fallback chain to prevent rendering a dead placeholder.

Written for commit 6b03679. Summary will update on new commits.

Review in cubic

@indent-zero

indent-zero Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Indent Zero is shutting down on August 7th. Please migrate over to Indent 2.0 to continue getting PR reviews.

PR Summary

Two small bug fixes bundled: repin the t3code pnpmDeps fixed-output hash on x86_64-linux (upstream's hash was generated on a non-Linux host, so every Linux build fails on the FOD mismatch) and drop the now-defunct gpt-codex-spark model from the OMP default fallback chain after it was removed from models.json. Also refactors one spec check off the yq dialect (kislyuk locally vs mikefarah in CI) onto a small Python helper.

  • overlays/default.nix: add a per-system pnpmDepsHashes.x86_64-linux pin, override llm-agents.t3code.pnpmDeps.outputHash, and rewire llm-agents.t3code-desktop.paths to the repinned build; other systems fall through to upstream unchanged.
  • config/omp/config.tpl.yml + config/omp/config.yml: replace the middle default chain entry __GPT_CODEX_SPARK__ / openai-codex/gpt-5.3-codex-spark with __GPT_CODEX__ / openai-codex/gpt-5.3-codex.
  • spec/llm_update_spec.sh: add omp_fallback_chain_tails_ok (Python heredoc) and swap the yq-based tail-strand assertion to When call; loosen the "OMP native retry" test to grep on the modelFallback / fallbackRevertPolicy flags.

Issues

3 potential issues found:

  • omp_fallback_chain_tails_ok silently passes when a role isn't parsed: any mis-match against the strict ^ role: "value"$ / ^ - "value"$ patterns hits continue and the role is skipped instead of failing (Latent; triggers when the config formatter changes indentation, quoting, or introduces inline comments). → Autofix
  • t3code-desktop override resets paths to a single-element list, which silently drops any additional inputs upstream's symlinkJoin may pick up in future releases (Latent; triggers when upstream t3code-desktop.paths gains a second entry — e.g. an icon set or launcher wrapper — on x86_64-linux only). → Autofix
  • The "uses OMP native retry fallback chains" test was loosened to grep only the two flag lines and no longer inspects fallbackChains.default contents — the very bug this PR fixes (a stale gpt-5.3-codex-spark in the default chain) would slip past this check now. → Autofix

CI Checks

Waiting for CI checks...


⚡ Autofix All Issues

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@shunkakinoki, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 53 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8f9ff6f4-0fb1-46c3-bb11-12610351bc13

📥 Commits

Reviewing files that changed from the base of the PR and between 2bdaef4 and 6b03679.

📒 Files selected for processing (4)
  • config/omp/config.tpl.yml
  • config/omp/config.yml
  • overlays/default.nix
  • spec/llm_update_spec.sh

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.

Comment thread overlays/default.nix
# it needs repointing at the repinned build rather than its own fix.
// prev.lib.optionalAttrs (prev.llm-agents ? t3code-desktop) {
t3code-desktop = prev.llm-agents.t3code-desktop.overrideAttrs (_: {
paths = [ t3code.desktop ];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latent: paths reset loses future symlinkJoin inputs.

This sets paths = [ t3code.desktop ], fully replacing whatever upstream's t3code-desktop symlinkJoin had. Today that's fine — upstream joins a single path — but if a future llm-agents.nix release adds a second entry (icon set, launcher wrapper, shared assets), it will be silently stripped on x86_64-linux only, while other systems keep the full join.

Consider preserving the tail with a map, e.g.:

t3code-desktop = prev.llm-agents.t3code-desktop.overrideAttrs (old: {
  paths = map (p: if p == prev.llm-agents.t3code.desktop then t3code.desktop else p) old.paths;
});

or at least asserting old.paths has length 1 so a future upstream expansion trips the build instead of silently narrowing it.

Comment thread spec/llm_update_spec.sh
elif base(model) in [base(entry) for entry in chain]:
index = [base(entry) for entry in chain].index(base(model))
else:
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Latent: silent pass on parse skew.

Any role whose model isn't matched by exact or base selector hits continue, so a formatter change that (a) drops a role from roles, (b) breaks the 4/6-space chain indentation, or (c) inlines a comment after the value will cause the role to be skipped and the test to pass vacuously. Since the two re.search().group(1) calls only hard-fail on the top-level modelRoles: / fallbackChains: headers, most drift goes undetected.

Suggest asserting the parsed structure is non-empty and treating an empty resolved chain as a failure, e.g.:

assert roles, "no modelRoles parsed"
assert chains, "no fallbackChains parsed"
...
chain = chains.get(role) or chains.get("default") or []
if not chain:
    stranded.append(f"{role} has no resolved fallback chain")
    continue

Comment thread spec/llm_update_spec.sh

It 'uses OMP native retry fallback chains instead of an extension'
When run bash -c "[ ! -e config/omp/fallback.ts ] && [ ! -e config/omp/fallback.json ] && yq -e '.retry.modelFallback == true and .retry.fallbackRevertPolicy == \"cooldown-expiry\" and .retry.fallbackChains.default == [\"openai-codex/gpt-5.6-luna\",\"openai-codex/gpt-5.3-codex-spark\",\"openai-codex/gpt-5.6-sol\"]' config/omp/config.yml >/dev/null"
When run bash -c "[ ! -e config/omp/fallback.ts ] && [ ! -e config/omp/fallback.json ] && grep -q 'modelFallback: true' config/omp/config.yml && grep -q 'fallbackRevertPolicy: \"cooldown-expiry\"' config/omp/config.yml"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: coverage regression on the default chain.

The previous assertion pinned .retry.fallbackChains.default to the three-entry expected list, which is exactly what would have caught openai-codex/gpt-5.3-codex-spark sitting stale in this file. The rewrite only checks the two flag lines, so this test would no longer flag the class of bug this PR fixes (removed placeholders / unresolvable model IDs in the default chain).

Since the reason for dropping yq was dialect divergence, one option is to reuse the new Python helper: either extend omp_fallback_chain_tails_ok to also assert chains["default"] equals the expected list, or add a small sibling helper and invoke it here with When call.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="spec/llm_update_spec.sh">

<violation number="1" location="spec/llm_update_spec.sh:39">
P2: When a role's model fails to match by exact or base selector, the loop just `continue`s without recording a failure. If a future formatting change drops a role from `roles`, breaks the chain indentation, or inlines a comment after a value, the role gets silently skipped and this test passes vacuously instead of flagging the parse regression. Add assertions that `roles`/`chains` are non-empty and treat an empty resolved chain as a failure rather than silently continuing.</violation>

<violation number="2" location="spec/llm_update_spec.sh:231">
P3: The default OMP fallback chain is no longer pinned: the old yq check asserted `fallbackChains.default == [openai-codex/gpt-5.6-luna, ..., gpt-5.6-sol]`, but the replacement only greps for `modelFallback: true` and `fallbackRevertPolicy`. A regression in the default chain's entries (or an empty `default:` block) now passes both tests, since `omp_fallback_chain_tails_ok` skips roles that resolve to nothing. Since issue #2360 was specifically about the contents of that chain, restore a concrete content check for the default chain instead of dropping it.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread spec/llm_update_spec.sh
index = chain.index(model)
elif base(model) in [base(entry) for entry in chain]:
index = [base(entry) for entry in chain].index(base(model))
else:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a role's model fails to match by exact or base selector, the loop just continues without recording a failure. If a future formatting change drops a role from roles, breaks the chain indentation, or inlines a comment after a value, the role gets silently skipped and this test passes vacuously instead of flagging the parse regression. Add assertions that roles/chains are non-empty and treat an empty resolved chain as a failure rather than silently continuing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At spec/llm_update_spec.sh, line 39:

<comment>When a role's model fails to match by exact or base selector, the loop just `continue`s without recording a failure. If a future formatting change drops a role from `roles`, breaks the chain indentation, or inlines a comment after a value, the role gets silently skipped and this test passes vacuously instead of flagging the parse regression. Add assertions that `roles`/`chains` are non-empty and treat an empty resolved chain as a failure rather than silently continuing.</comment>

<file context>
@@ -4,6 +4,49 @@
+        index = chain.index(model)
+    elif base(model) in [base(entry) for entry in chain]:
+        index = [base(entry) for entry in chain].index(base(model))
+    else:
+        continue
+    if index == len(chain) - 1:
</file context>

Comment thread spec/llm_update_spec.sh

It 'uses OMP native retry fallback chains instead of an extension'
When run bash -c "[ ! -e config/omp/fallback.ts ] && [ ! -e config/omp/fallback.json ] && yq -e '.retry.modelFallback == true and .retry.fallbackRevertPolicy == \"cooldown-expiry\" and .retry.fallbackChains.default == [\"openai-codex/gpt-5.6-luna\",\"openai-codex/gpt-5.3-codex-spark\",\"openai-codex/gpt-5.6-sol\"]' config/omp/config.yml >/dev/null"
When run bash -c "[ ! -e config/omp/fallback.ts ] && [ ! -e config/omp/fallback.json ] && grep -q 'modelFallback: true' config/omp/config.yml && grep -q 'fallbackRevertPolicy: \"cooldown-expiry\"' config/omp/config.yml"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The default OMP fallback chain is no longer pinned: the old yq check asserted fallbackChains.default == [openai-codex/gpt-5.6-luna, ..., gpt-5.6-sol], but the replacement only greps for modelFallback: true and fallbackRevertPolicy. A regression in the default chain's entries (or an empty default: block) now passes both tests, since omp_fallback_chain_tails_ok skips roles that resolve to nothing. Since issue #2360 was specifically about the contents of that chain, restore a concrete content check for the default chain instead of dropping it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At spec/llm_update_spec.sh, line 231:

<comment>The default OMP fallback chain is no longer pinned: the old yq check asserted `fallbackChains.default == [openai-codex/gpt-5.6-luna, ..., gpt-5.6-sol]`, but the replacement only greps for `modelFallback: true` and `fallbackRevertPolicy`. A regression in the default chain's entries (or an empty `default:` block) now passes both tests, since `omp_fallback_chain_tails_ok` skips roles that resolve to nothing. Since issue #2360 was specifically about the contents of that chain, restore a concrete content check for the default chain instead of dropping it.</comment>

<file context>
@@ -180,12 +223,12 @@ End
 
 It 'uses OMP native retry fallback chains instead of an extension'
-When run bash -c "[ ! -e config/omp/fallback.ts ] && [ ! -e config/omp/fallback.json ] && yq -e '.retry.modelFallback == true and .retry.fallbackRevertPolicy == \"cooldown-expiry\" and .retry.fallbackChains.default == [\"openai-codex/gpt-5.6-luna\",\"openai-codex/gpt-5.3-codex-spark\",\"openai-codex/gpt-5.6-sol\"]' config/omp/config.yml >/dev/null"
+When run bash -c "[ ! -e config/omp/fallback.ts ] && [ ! -e config/omp/fallback.json ] && grep -q 'modelFallback: true' config/omp/config.yml && grep -q 'fallbackRevertPolicy: \"cooldown-expiry\"' config/omp/config.yml"
 The status should be success
 End
</file context>

@shunkakinoki
shunkakinoki merged commit d4530c6 into main Aug 12, 2026
27 of 29 checks passed
@shunkakinoki
shunkakinoki deleted the claude/fix-t3code-pnpm-hash branch August 12, 2026 16:21
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