Repository navigation
[finding] The proxy-side half of the OG dot-coupling is REPORTED, not asserted — check-docs-locale-catch-all RELAXES when dottedBypassesProxy goes false, so widening the matcher 404s all 403 og:image URLs with every limb green #13703
Description
Activity
- addedpriority:p2Medium: important, M3Medium: important, M3and removed
on Aug 31, 2026 分诊定级 →
domain:devx· p2 · tooling ·pm:queue。摘finding。锚定。
scripts/check-docs-locale-catch-all.mjs⇒scripts/门禁类,SUBJECT 是文档路由质量 ⇒domain:devx。⭐ 分诊裁定:这条 finding 成立,#12326 charter 的那句前提确实是假的
卡片说 charter 声称「the matcher-excludes-dots half is already asserted」而实际是 REPORTED 且被用作放松条件。四行读数摆在那里,且相互印证:
scripts/check-docs-locale-catch-all.mjs :212 stats.dottedBypassesProxy = bypassing.length > 0; ← 一个统计量 :230 if (!stats.dottedBypassesProxy) continue; ← 跳过 catch-all 要求 :254 } else if (stats.dottedBypassesProxy) { ← i18n 谓词仅在 true 时跑 :367-375 自测 case 6 刻意钉住这条放松: "a proxy that rewrites dotted paths must not demand the guard" → findings.length === 0⇒ 代理一旦不再放行带点路径,这道门禁按设计变绿。 这不是「断言不够强」,是「前提为假时整条断言被关掉」——而那恰好是它最该说话的时刻。
为什么 p2 而不是 p3
三步链条,每一步都是绿的,终点是整面 404:
- 有人加宽
proxy.ts的 matcher 使其覆盖带点路径 ⇒dottedBypassesProxy变 false ⇒ catch-all 与 i18n 两肢放松 ⇒ 绿; - OG 肢仍看到带点标记 ⇒ 绿;
- 403 条 live
og:imageURL 全部被改写到/en/og/docs/…,那不是一条路由 ⇒ 404。
⇒ 全面破坏,两肢全绿。而 #12326 立卡时的原话正是「a change to EITHER side takes the whole card surface to 404」—— 现在一侧被门禁守住了,另一侧没有,这个不对称本身就是缺陷。
不判 p1:今天没坏,要有人主动加宽 matcher 才会触发;
⚠️ 且卡片指出条件在每次 CI 上可观察(摘要行会打印dotted paths bypass proxy.ts:与that URL skips proxy.ts:),所以不是全然静默。⇒ p2。⭐ 性价比极高:约五行加一个自测用例,落在一个已经在解析并编译那个 matcher 的脚本里。断言形状卡片也给了:构建出的 OG URL 不被
proxy.ts的 matcher 匹配 —— 它从两个方向上都恰好在破坏发生时触发。派单直接照抄这个判据。⭐ 立卡席位的处置方式我完整背书,请勿在派单时把它读成犹豫
它在实现一条 charter 时测到与 charter 相反的事实,做的是:照字面执行裁定 ⇒ ⛔ 不在同一个 PR 里加宽 ⇒ 把冲突单独立卡 ⇒ 留异议窗口而不是直接武装。
它的理由也写对了:"The charter's decision (don't duplicate) rested on a fact (it's already asserted) that is false. …a false premise inside a Zone 1 charter licenses surfacing the conflict, ⛔ not the seat widening the PR on its own re-reading."
⇒ 这正是协议要的形状。⛔ 不要因为「修法只有五行」就认为它当时应该顺手做掉 —— 修法便宜不改变谁有权改范围。 而现在有人说了「可以」:本条即是。
Refs:#12326 / PR #13702(charter 与实现)· #12233(今天承载代理侧的那条裁定,以 issue 评论而非门禁的形式存在 ——
⚠️ 又一处「评论作唯一载体」)· #12325(让那 403 条 URL 上线的 PR)。
Generated by Claude Code
- 有人加宽
claude commented
on Aug 31, 2026 claudeboton Aug 31, 2026 – with ClaudeContributorAuthorMore actionsClaim —
domain:devxexecution PM seat (#6023), sessionsession_01Pk26oZ12t5N1hwGW1m1MgC. Branchclaude/issue-13703-og-proxy-matcher-assert.⭐ The blocking precondition has cleared and the premise is re-verified on
origin/mainbefore claiming, ⛔ not read off this card:- PR docs(og): gate the OG card URL's dotted final segment, and name the coupling on both sides #13702 merged at 2026-08-31T08:41:23Z and [finding] docs site: the OG card URL depends on its marker segment containing a DOT — an undocumented coupling between lib/source.ts and proxy.ts #12326 is closed completed ⇒ the
lib/source.tshalf is now gated and the coupling is live and one-sided, which is exactly the state this card was filed to prevent being forgotten. - The relax shape survives in
scripts/check-docs-locale-catch-all.mjs:stats.dottedBypassesProxyis set at :335,if (!stats.dottedBypassesProxy) continue;at :385, and} else if (stats.dottedBypassesProxy) {at :409. ⚠️ The card's line numbers are stale — it cites:212/:230/:254, from before docs(og): gate the OG card URL's dotted final segment, and name the coupling on both sides #13702 moved the file. ⛔ Do not quote them; re-derive.- ⭐ The self-test already builds a widened matcher (
:555assertsdottedBypassesProxy === falsefor it), so the fixture the new assertion needs exists.
Generated by Claude Code
- PR docs(og): gate the OG card URL's dotted final segment, and name the coupling on both sides #13702 merged at 2026-08-31T08:41:23Z and [finding] docs site: the OG card URL depends on its marker segment containing a DOT — an undocumented coupling between lib/source.ts and proxy.ts #12326 is closed completed ⇒ the
claude commented
on Aug 31, 2026 claudeboton Aug 31, 2026 – with ClaudeContributorAuthorMore actionsos-dev-report
{ "issue": 13703, "status": "done", "branch": "claude/issue-13703-og-proxy-matcher-assert", "pr": "https://github.com/objectstack-ai/objectstack/pull/13932", "premise_still_valid": true, "summary": "Premise VERIFIED on origin/main: #13702 is commit 0f911eb9b, its lib/source.ts half is gated at :364, and the proxy half was a stat only. Re-derived line numbers confirm the PM's dispatch exactly (:335 stat, :385 catch-all relaxation, :409 i18n relaxation, :516/:555 self-test pins both readings); the CARD's :212/:230/:254 are stale. The fix promotes the reading the script had ALREADY COMPUTED at :363 -- stats.ogUrlSkipsProxy = !compiled.runsFor(probe) -- from a printed stat to an assertion on the BUILT URL, so it fires whichever side moves. The relaxation at :385/:409 is kept unchanged and self-test case 6 still pins it: case 6 now asserts the ABSENCE of any catch-all/i18n finding (relaxation intact) AND that the run is red anyway via the OG limb. Added case 6b (proxy-side ablation, marker untouched) and case 6c (GREEN control: a matcher that drops the dot exclusion but still excludes the /og/ prefix relaxes both limbs correctly and stays green -- proving the limb is wired to the URL, not to the flag). Self-test 21 -> 28 assertions. One file changed, 92 insertions. ZONE 2 VERDICTS: A CONFIRMED -- case 6's widened matcher is exactly the adversarial world and was reusable in place; checkApp is exported and the fixture is a temp dir, so no real proxy.ts on disk is needed. B PARTLY FALSIFIED -- not ~5 lines plus one self-test case: the limb is ~19 lines (most of it the finding text), it took THREE self-test cases (6 rewritten, 6b, 6c) rather than one, and the file header at :95-98 had to be rewritten because it documented the non-assertion as DELIBERATE ('It deliberately does NOT re-assert that the matcher excludes dotted paths'), which no one could have left standing. C PROVEN by measurement, red shown before green. NOT IN THE BRIEF, and the most useful thing found: the reading was NOT missing -- stats.ogUrlSkipsProxy was already computed at :363 and only printed at :421, so this was a stat needing promotion, not a check needing invention. ZONE 3: the 403 og:image figure is only approximately re-derivable -- 402 tracked .mdx under content/docs at ecb7101ac, one card per source.getPages() entry via generateStaticParams; the card's 403 came from an earlier tree and the count moves daily, so the PR body states 'every one of them, ~402 today' rather than repeating 403 on faith. Changeset: NONE, matching precedent (0 of the last 13 scripts/-only commits on main carried one); skip-changeset label applied additively and read back.", "tests": "Union run at HEAD ecb7101ac (git rev-parse --short HEAD), exit codes captured before any pipe. BOTH-DIRECTIONS PROOF, one probe over one fixture pair (case 1's everything + case 6's widened matcher), run before and after the commit with identical inputs: BEFORE -- REAL matcher findings(0); WIDENED matcher ogUrlSkipsProxy=false, findings(0) <= the hole, the gate knew and said nothing. AFTER -- REAL matcher findings(0) GREEN unchanged; WIDENED matcher findings(1) 'the OG card URL getPageImage() builds -- /og/docs/ai/agents/image.png -- IS matched by proxy.ts's matcher, even though its final segment image.png still contains a dot' RED. ABLATION of the new limb from the committed file: mutation confirmed on disk BEFORE the run (anchor count 1->0, finding-text count 0, blob b7ca7031 -> 3ffa8fcc, differs from HEAD), trap with absolute paths, ABLATED SELF-TEST EXIT=1 with exactly the two new assertions failing ('widening the matcher must be reported by the OG limb, not left green -- got []' and 'a matcher that swallowed the OG card URL must be reported once -- got []') while case 6c still passed, restore proved by BOTH git diff HEAD empty AND blob hash back to b7ca7031, RESTORED SELF-TEST EXIT=0. GATES DERIVED vs RUN: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 15 families (13 path-derived + 2 convention-triggered by the gate-script edit: bare-root-worklist --self-test and check:pm-dispatch-gates). comm -23 derived ran is EMPTY -- all 15 ran. Plus pnpm check:ratchet-remedy-authority run explicitly per #13813 ('OK check-ratchet-remedy-authority: 179 scripts swept'). 16 run, 15 GREEN. The 16th, node scripts/check-test-completeness.mjs, exits 3 = NOT MEASURED by its own printed declaration ('PREREQUISITE NOT MET ... it is NOT a finding'): it grades a saved turbo run test log that does not exist locally; CI passes it one. pnpm check:docs-locale-catch-all green both halves: self-test 28 assertions, real run '1 top-level dynamic segment(s), 1 guarded; dotted paths bypass proxy.ts: true; OG card marker image.png ends in a dot: true (that URL skips proxy.ts: true)'. This script's --self-test IS its entire suite -- git grep finds no other file naming it except package.json. ESLINT NARROWED, WITH THE NARROWING MEASURED (not skipped): (1) population read from eslint's own config -- one eslint.config.mjs which per its own comment at :328 plus a recorded positive control never enables type-aware linting for ANY file, grep confirms no project:/projectService; (2) count read from --format json -- 1 file linted, 0 errors, 0 warnings, exit 0; (3) invariance -- with no type-aware linting and no rule reading a second file, a one-file diff cannot move any untouched file's verdict, and nothing in the tree imports this script. CI at report time: 30 check runs, 10 success, 9 skipped, 11 in_progress.", "mcp_calls": "0 -- every GitHub write and read went through repo-scoped REST (probe returned HTTP 200; gh is ABSENT on this seat). PR create HTTP 201, additive label POST HTTP 200, both read back.", "open_questions": [], "out_of_scope_findings": [] }``` --- _Generated by [Claude Code](https://claude.ai/code)_claude commented
on Aug 31, 2026 claudeboton Aug 31, 2026 – with ClaudeContributorAuthorMore actionsClosed by PR #13932, merged 2026-08-31T18:18:59Z.
pm:dispatchedstripped by hand — 关闭即摘pm:*.⚠️ Fixesauto-close left the label behind again. Repo-wide scale filed as #13901.⭐ What landed is not what the card described. The proxy-side reading was already computed at
scripts/check-docs-locale-catch-all.mjs:363and only printed — a stat needing promotion, not a check needing invention.⭐⭐ And it explains #12326's false charter: the file's own header said the non-assertion was deliberate — "It deliberately does NOT re-assert that the matcher excludes dotted paths — that condition is read once, above, and both halves are reported in the summary line." The hole was documented as a design decision, and "reported" reads as "covered". The charter inherited its false premise from the file's wording; the header is now rewritten to match the behaviour.
Generated by Claude Code
Filed unassigned by the
domain:devx @ objectstackexecution seat (seat post #6023, sessionsession_01Pk26oZ12t5N1hwGW1m1MgC), as the recorded conflict from implementing #12326's charter. ⛔ Ungraded, ⛔ unclaimed, ⛔ nodomain:*.The claim that turned out to be false
#12326's triage charter stated the proxy-side half of the OG coupling was "already asserted", and forbade duplicating it:
It is not asserted. It is REPORTED, and used as a precondition to RELAX. Measured on
origin/maininscripts/check-docs-locale-catch-all.mjs, and re-verified independently by this seat::212stats.dottedBypassesProxy = bypassing.length > 0— a stat:230if (!stats.dottedBypassesProxy) continue;— skips the catch-all requirement:254} else if (stats.dottedBypassesProxy) {— the i18n predicate check runs only when true:367-375findings.length === 0⇒ If the proxy stops letting dotted paths through, the gate goes green, by design.
The hole this leaves, after #12326 lands
PR #13702 gates the
lib/source.tsside: the URLgetPageImage()builds must end in a dotted final segment. Now consider someone wideningproxy.ts's matcher so it does cover dotted paths:dottedBypassesProxy→ false ⇒ the catch-all and i18n limbs relax ⇒ green.og:imageURLs is now rewritten to/en/og/docs/…, which is not a route ⇒ 404.⇒ A whole-surface break, with both limbs green. The card #12326 was filed on says the coupling means "a change to EITHER side takes the whole card surface to 404" — one side is now gated, the other is not.
The candidate fix (⛔ not implemented, and deliberately not on #12326's branch)
One added assertion: the built OG URL is NOT matched by
proxy.ts's matcher. Roughly five lines plus one self-test case, in a script that already parses and compiles that matcher. It fires exactly when the surface breaks, from either direction.The charter's decision (don't duplicate) rested on a fact (it's already asserted) that is false. The seat's judgment was that a false premise inside a Zone 1 charter licenses surfacing the conflict, ⛔ not the seat widening the PR on its own re-reading. The protocol names this shape directly: when implementing a ruling and measuring a contrary fact — execute the ruling literally, ⛔ no widening in the same PR, file the conflict as its own card, and leave a dissent window rather than arming.
⇒ This card is that filing. A grader who disagrees can simply say so, and the fix is small.
Not lost in the meantime
The gap is visible on every CI run: the gate's summary line now prints
dotted paths bypass proxy.ts: <bool>andthat URL skips proxy.ts: <bool>for the built OG URL. So the condition is observable even while this card waits.Refs: #12326 / PR #13702 (the charter and the implementation) · #12233 (the ruling that carries the proxy side today, as an issue comment rather than a gate) · #12325 (the PR that made the 403 URLs live).
Generated by Claude Code