fix(ai): honor the documented GJC_NO_STRICT bypass - #3305
Conversation
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Verdict: APPROVE — exact head 7709d367764b8b21a4a793ed822b911ee0a52065
Same PI_ → GJC_ documented-but-unread drift class as #2827/#2943/#3041/#3171/#3245/#3296, gating strict-schema enforcement bypass. Documented GJC_NO_STRICT alias verified as a genuine, correctly-behaving boolean.
Checks performed:
- Pre-fix defect confirmed:
docs/ai-schema-normalize.md:32documentsGJC_NO_STRICT; pre-fixadapt.ts:14read only$flag("PI_NO_STRICT")— the documented name was a silent no-op, matching the PR's claim. $pickflagsemantics verified directly (packages/utils/src/env.ts:284-290): iterates keys in order, returns the boolean value of the first key with a non-empty value (not first-truthy) — i.e., an explicitGJC_NO_STRICT=0correctly wins over a setPI_NO_STRICT=1and resolves tofalse, matching the PR's "canonical name wins over a truthy legacy value" test case exactly.- Boolean-vs-presence contract choice justified: unlike #3296 (
GJC_AUTH_NO_BORROW, presence-based because=0re-enabling token borrow would be the unsafe direction), hereGJC_NO_STRICT=0meaning "stay strict" is the safe failure direction, so routing through the existing$pickflagboolean contract (rather than a bespoke presence check) is the correct choice — the PR explicitly calls out and reasons through this distinction rather than copy-pasting the #3296 pattern blind. - Docs reference-page gap:
GJC_NO_STRICTadded todocs/environment-variables.md's OpenAI-code table, consistent with that file's stated scope ("derived from current code paths… documents only active behavior"). - Tests: hermetic — each scenario spawns a fresh process via
no-strict-probe.tssinceNO_STRICTis a module-level constant, correctly avoiding a stale-import false pass. Six cases: default-strict, documented-name honored, legacy-name honored, case-insensitive boolean spellings, explicit falsey documented value, and canonical-wins-over-truthy-legacy. Proof-first claim (reverting to legacy-only fails 3/6) is consistent with the diff. - CI: all applicable checks pass on the exact head, including the new
test:packages/ai/test/no-strict-env.test.tsjob explicitly green. - Novelty: no other open PR touches
adapt.ts'sNO_STRICTresolution; not a duplicate. - No automated reviewer comments present.
No merge action taken — read-only review verdict only.
—
[repo owner's gaebal-gajae (clawdbot) 🦞]
7709d36 to
d0efab7
Compare
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Verdict: APPROVE — exact head d0efab7e95bdd58e254423a21ff8b8bf97ce780e
Rebase-only refresh of the prior exact-head review at 7709d367764b8b21a4a793ed822b911ee0a52065 (APPROVE).
Checks performed:
- Content identity confirmed, not assumed:
diff <(git show 7709d367 --format= -- docs/environment-variables.md packages/ai/src/utils/schema/adapt.ts packages/ai/test/fixtures/no-strict-probe.ts packages/ai/test/no-strict-env.test.ts) <(git show d0efab7e --format= -- docs/environment-variables.md packages/ai/src/utils/schema/adapt.ts packages/ai/test/fixtures/no-strict-probe.ts packages/ai/test/no-strict-env.test.ts)is empty — the PR's own commit is byte-for-byte identical to the version the prior review validated. CHANGELOG.md is intentionally excluded from this file set: its diff context shifted because independently-mergeddevcommits inserted new entries above the same insertion point, which is expected rebase churn, not a content change to this PR's own line. - Rebase mechanics verified:
git merge-baseconfirms this PR carries the same single commit both before and after the rebase; every additional commit between the two heads is an independently-mergeddevcommit (own-PR-numbered), not new work folded into this PR. - Exact-head terminal CI: 17 success / 0 failure / 0 pending / 5 skipped (total 22). No pending/queued checks remain.
Prior technical analysis still applies verbatim to this head.
—
[repo owner's gaebal-gajae (clawdbot) 🦞]
d0efab7 to
8244392
Compare
Yeachan-Heo
left a comment
There was a problem hiding this comment.
MERGE_READY (verdict reconciliation — exact head)
Reattaching the existing durable verdict to the current exact head 824439238ddd2211f023b2c24ffc7bc1d8503216 (base dev @ fbb5670709a1b1e04abddd1cc4827143fb63b876). This is a reconciliation, not a new review: the prior APPROVED was submitted at d0efab7e, and the head moved by rebase only.
Evidence:
- Normalized patch of
d0efab7eand82443923(file list + hunk bodies, hunk headers excluded) is byte-identical — SHA2565a95ab30…for both. File-level shape is unchanged:docs/environment-variables.md +1-0,packages/ai/CHANGELOG.md +1-0,packages/ai/src/utils/schema/adapt.ts +2-2,packages/ai/test/fixtures/no-strict-probe.ts +5-0,packages/ai/test/no-strict-env.test.ts +59-0,packages/coding-agent/src/internal-urls/docs-index.generated.ts +1-1. - Only the parent changed (
b853f15d→fbb56707). No source, test, or docs delta. - Exact-head checks: 17 success, 6 skipped, 0 failure.
mergeable_state: clean.
Verdict carries forward unchanged. Merge remains gated on dev returning green (current dev @ fbb56707 is red).
—
[repo owner's gaebal-gajae (clawdbot) 🦞]
8244392 to
9925a94
Compare
docs/ai-schema-normalize.md:32 says adaptSchemaForStrict "consults
GJC_NO_STRICT (env GJC_NO_STRICT) for the global bypass". adapt.ts:14 read
$flag("PI_NO_STRICT"), so the documented name was a silent no-op.
Same PI_ -> GJC_ drift class as Yeachan-Heo#2827 / Yeachan-Heo#2943 / Yeachan-Heo#3041 / Yeachan-Heo#3171 / Yeachan-Heo#3245 / Yeachan-Heo#3296.
The consequence here is a stuck escape hatch: this flag is what turns off
OpenAI-style strict function-schema enforcement, so an operator working around a
provider that rejects strict schemas set the documented name and strict mode
stayed on.
Resolved with $pickflag("GJC_NO_STRICT", "PI_NO_STRICT"). $pickflag applies
the same boolean contract $flag already used — first non-empty key decides,
case-insensitive 1/true/yes/on — so the legacy behavior is unchanged and the
canonical name simply takes precedence. This is a bypass rather than a privacy
opt-out, so the boolean contract is the right shape here, unlike Yeachan-Heo#3296 where an
explicit 0 must not re-enable borrowing.
Also lists GJC_NO_STRICT in docs/environment-variables.md. That page states it
is derived from the current code paths and documents only active behavior, but
this knob was taught only in the schema-normalisation note.
Regression: six cases covering default-on strict mode, each name honoured, the
documented boolean spellings, an explicit falsey value, and the canonical name
winning over a truthy legacy value. Reverting to the legacy-only read fails
three of them.
9925a94 to
49be461
Compare
Yeachan-Heo
left a comment
There was a problem hiding this comment.
Verdict: APPROVE — exact head 49be461ef0ba39486fded9987d206dcfa297db56.
Behavior and release-surface revalidation:
NO_STRICTnow resolves with$pickflag("GJC_NO_STRICT", "PI_NO_STRICT"), so the documented canonical name works, the legacy alias remains supported, and the first non-empty canonical value wins even when explicitly falsey. That safe-direction boolean contract is appropriate for a bypass flag.- Six fresh-process scenarios cover default strict mode, both names, case-insensitive truthy spellings, explicit false, and canonical-over-legacy precedence.
- The semantic rebase lands on current
devparentf3a34362243e9ddde049844094e52547557328f9. The changelog entry is underUnreleased; the environment reference was merged with current content and the internal docs index regenerated from source rather than conflict-resolved by hand. - Local focused suite: 6/6 passed. AI typecheck, Biome, generated-doc public-sync, and diff checks passed.
- Exact-head CI is terminal with every reported check SUCCESS or SKIPPED, including the focused NO_STRICT test plus both AI and coding-agent checks.
- Mergeability is
MERGEABLE/CLEAN, and the exact remote head/base were re-pinned immediately before this verdict.
— GJC provider/trust review, 2026-07-28
Another documented-but-unread name, found by checking the canonical env reference against what other docs teach.
Problem
Same
PI_→GJC_drift class as #2827 / #2943 / #3041 / #3171 / #3245 / #3296, and like #3296 the drift costs something concrete: this flag is the escape hatch that turns off OpenAI-style strict function-schema enforcement. An operator working around a provider that rejects strict schemas sets the documented name, and strict mode stays on.Fix
$pickflag("GJC_NO_STRICT", "PI_NO_STRICT").$pickflagapplies the same boolean contract$flagalready used — first non-empty key decides, case-insensitive1/true/yes/on— so legacy behavior is unchanged and the canonical name simply takes precedence.The boolean contract is right here, unlike #3296: this is a bypass, so
GJC_NO_STRICT=0meaning “stay strict” is the safe direction. In #3296 the same shape would have let=0silently re-enable reading a token out of another application, which is why that one stayed presence-based. Calling that out because the two look superficially identical.Also: a reference-page gap
GJC_NO_STRICTwas taught only in the schema-normalisation note.docs/environment-variables.mdopens by saying it is “derived from current code paths” and “documents only active behavior”, so a knob read inpackages/ai/src/**belongs there. Added.Tests
no-strict-env.test.tsruns each scenario in its own process, becauseNO_STRICTis a module-level constant: strict on by default, each name honoured, the documented boolean spellings accepted case-insensitively, an explicit falsey value respected, and the canonical name winning over a truthy legacy value.Proof-first: reverting to the legacy-only read fails three of the six.
Verification
bun run checkinpackages/ai(whole-package biome + tsc): exit 0. New suite: 6 pass / 0 fail. Strict-named suites: 277 pass / 0 fail. Docs index regenerated;check:public-syncin sync.