Skip to content

check-opencode-metered-providers: entry-point check uses resolve() not realpath — invoked via any symlink the PAYG guard silently no-ops with exit 0 #4694

Closed

Description

@dezren39

Summary

bin/check-opencode-metered-providers.mjs — the guard that fails CI/local checks when a metered (PAYG) provider (deepseek, cloudflare-*) is selected without an adjacent // payg-exception: marker — silently no-ops when invoked through any symlink. Its entry-point check at line 359 (origin/main 03b5817dc) is:

if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
  await main();
}

path.resolve() only normalizes the invoked path; it does not resolve symlinks, while fileURLToPath(import.meta.url) is the module's real path. Invoked via a symlinked file or through a symlinked directory (e.g. the fleet's own ~/code -> ~/git/code checkout alias that caused #2083), the two never match, main() never runs, and the process exits 0 with no output — a metered selection that should fail the guard reports as passing.

This is the same bug class as #2083 / #2235 / #4325, in a fifth file and in a different syntactic shape (reversed comparison, resolve() instead of pathToFileURL(...).href), so the repo-wide grep in #4325's acceptance criteria — written against the pathToFileURL shape — does not catch it. A grep for fileURLToPath(import.meta.url) entry-point comparisons in bin/ at origin/main shows this is the only remaining instance in that shape (the other hits are dirname(...) path derivations, test helpers, or the already-fixed realpathSync comparison in bin/provider-catalog-sync.mjs:936).

Evidence (laptop probe ~/git/fleet/probe-2026-11-02-laneB-meteredentry/, file extracted verbatim from origin/main 03b5817dc, md5 72912190ea823ae65c7ef6898f9b64fb both sides)

Config under test: { "model": "deepseek/deepseek-chat" } (a metered selection with no payg-exception marker).

  • Direct: node real/check.mjs config.jsonc → stderr Metered-provider config guard failed … metered provider "deepseek" selected by $.model …, exit 1 — the guard ran and failed the config.
  • Via a file symlink: node link.mjs config.jsonc → no output, exit 0 — the guard never ran; the metered selection silently "passes".
  • Via a symlinked directory: node linkdir/check.mjs config.jsonc → no output, exit 0 — same silent no-op.
  • Control, clean config direct (meta/… model): prints Metered-provider config guard passed, exit 0 — the direct path works as designed.

Why it matters

This guard is the automated backstop for the house rule that PAYG providers are a reserve, never a routine route. A check that exits 0 without checking anything is worse than no check: any wrapper, symlink farm, or checkout-alias invocation (the exact layout that already burned the fleet in #2083) turns a failing metered selection into a green pass, and nothing anywhere records that the guard was skipped.

Distinct from #4325 (open): that issue names four files (bin/codedb-bridge.mjs, the supabase/cloudflare exporters, the claimant installer), all in the pathToFileURL(process.argv[1]).href shape. This file is not named there and its reversed resolve(...) === fileURLToPath(...) shape evades that issue's grep-based acceptance criterion; it needs its own fix (or an explicit addition to #4325's scope plus a widened grep).

Acceptance criteria

Related: #2083 (original instance + proven fix pattern), #2235 (closed by #4123), #4325 (open — the other four instances, different shape), #2350 (open — meta-exporter instance).

Found by new-issue-creator-code-b (lane B) this run.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/normalOrdinary priority (the default)
    effort/xsTrivial: expected under 30 minutes of work
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    on Nov 3, 2026
  2. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · state: filed

  3. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  4. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: picked_up
    task: 6673 (repo-health-code-pilot, lane 8ac61616-3815-4e7b-b33d-840a3829154a)
    state: claimed via atomic queue claim at 2026-11-02 19:02 CST; issue #4694 is OPEN with no hard-stop labels. Search found no PR referencing #4694; #4668 is Drew-authored and changes only the unrelated opencode-config-guard workflow, so it will not be touched.
    next: verify the reported symlink no-op against fresh main, then route the narrowly scoped fix and regression tests without duplicating #4325.

  5. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: done
    task: 6673 (repo-health-code-pilot, lane 8ac61616-3815-4e7b-b33d-840a3829154a)
    state: triaged; reproduced and routed as follow-up task 6674 (small_fix).
    evidence (fresh origin/main 9dfc29e, 2026-11-03): direct invocation on deepseek/deepseek-chat exits 1 with the metered-provider failure; file-symlink and symlinked-directory invocations both exit 0 silently; direct clean-config control exits 0 with pass output.
    related work: PR #4583 for #4325 is OPEN and changes four other entry points, not this guard. Cross-linking #4694 into #4325 for visibility; follow-up scope stays separate.
    next: task 6674 changes only this guard and its regression tests; #4694 remains OPEN pending that fix.

  6. added
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    task/triageQueue packet kind: verify, reproduce, and route this item
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 3, 2026
  7. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  8. added a commit that references this issue on Nov 3, 2026
  9. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done — PR #4697 merged, issue #4694 closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    app/check-opencode-metered-providersbin/check-opencode-metered-providers.mjs metered PAYG provider guardapp/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)queue/queuedHas a queued task in the Supabase agent queuesource/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions