') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); docs(pm-dispatch): state the duty-7 reader-count method's applicability domain by zhuangjianguo · Pull Request #13580 · objectstack-ai/objectstack · GitHub
Skip to content

docs(pm-dispatch): state the duty-7 reader-count method's applicability domain - #13580

Closed
zhuangjianguo wants to merge 1 commit into
mainfrom
claude/issue-13304-reader-count-domain
Closed

docs(pm-dispatch): state the duty-7 reader-count method's applicability domain#13580
zhuangjianguo wants to merge 1 commit into
mainfrom
claude/issue-13304-reader-count-domain

Conversation

@zhuangjianguo

Copy link
Copy Markdown
Collaborator

Fixes#13304

Governed surface (.claude/**) — this PR stays DRAFT. Not flipped ready, not enqueued, no auto-merge armed, no review submitted. Landing is the maintainer's, by hand.

The defect, re-measured on current main

The reader half of standing duty ⑦ counts readers with git grep -I -n -w on the bare column name, and its only calibration case is sys_share_link.email_allowlist. I re-ran the pinned method over the whole population at base 889ec5b42:

  • Population reproduced exactly: 10 objects / 121 columns (plugin-security 7 files, plugin-sharing 3).
  • Calibration control still holds: 7 raw hit lines − 4 *.generated.ts (rule ①) = 3. The instrument is calibrated as documented.
  • And that is the whole problem. Measured raw hit lines across all 121 keys:
statisticraw hit lines
calibration case email_allowlist7
population minimum7 (the calibration case is the minimum)
median key1366
maximum (name, 5419 files)75985

The pinned control is not merely an easy key — it is the single easiest key in its own population. A calibration that passes there authorizes nothing about managed_by (891 lines / 154 files) or expires_at. The duty's no zero-reader keys output was therefore a false green over keys the instrument cannot discriminate on at all.

⭐ One in-passage correction the measurement forced, named here because an unnamed drive-by fix is unreviewable sprawl: the old text's own parenthetical read `name` 这类列名到处匹配,未校准的仪器平凡地回 0 — it names exactly the right key and states the wrong failure direction. name does not trivially return 0; it returns 75985. A generic name floods the count and can never return zero, which is precisely why the duty is blind to its own subject. Corrected in place, same passage, same defect class, no new file surface.

The repair

The honest applicability-domain route, with a derived criterion rather than a hand-listed key set:

  1. Applicability domain. Only a key whose every hit line is adjudicated by rules ①②③ produces a reading. Everything else is recorded NOT MEASURED — explicitly neither0nor "has readers". The criterion is the method's own unit of work (a hand-read hit), so it re-derives itself per run and cannot rot: a key that becomes generic drops out automatically, one that becomes distinctive enters.
  2. A negative control, run in the same round.managed_by must read as unadjudicable. This is the half the method never had: the positive control on the most distinctive key cannot fail for the reason the duty exists to fear, whereas a generic-name control that ever came back with a handful of hits would immediately expose a broken or over-excluding probe.
  3. The output is always two halves已测 N (零读者 Z) · 未测 121-N — and 无零读者键 is banned outright, with the standing correction that ⑦'s historical greens are not evidence for that conclusion.
  4. The family's transferable sentence lands here verbatim, in its extended form.

Why the two forbidden routes were not taken

  • More exclude paths. Adds none. The diff touches no path filter; the probe is byte-identical. Suppressing noise would just relocate the calibration to the next easy key, which is the same defect one step along.
  • Shrink 121 to 17 and declare 100%. No sample is narrowed and no coverage percentage is claimed. The reading is required to carry the unmeasured remainder in the same breath — that is what "输出恒两半" and the NOT MEASURED rule enforce. There is deliberately no fixed key list and no magic threshold constant in the text.

Route note (declaration-vs-use): I judged the strong form out of reach here and say so rather than fake it. Resolving "is this managed_by about this column or a homonym" needs real symbol resolution, not grep; nothing runnable per-seat in a few minutes does it, and a text pretending otherwise would be a second uncalibrated instrument. The domain statement is the deliverable the grading offers as the alternative, and it is the one that stays honest under its own failure.

Line budget

Word-compression inside the touched passage only. ⛔ No re-wrap-as-funding.

itembeforeafterdelta
dispatch-runbook.md lines271278+7
ratchet ceiling2782780
headroom70−7
widest line (bytes)1201200
files changed1+10 / −3

Exactly the funded 7, ceiling untouched, no line over the file's 120-byte convention.

Gate verdicts

Derived from the actual diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (7 families). Union re-run at final head 0959367f1 on a clean tree; every exit captured before any pipe.

✓ check-agent-test-spelling: 0 violations — 413 file(s) · 5064 bare `--` token(s) · 1284 launcher-rooted run(s)
✓ doc authoring guard: 48 published skill files clean — no internal issue-id references.
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1451 TS blocks judged clean
✓ check-governed-merges --self-test: 230 assertions
✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/dispatch-runbook.md is 278 lines (ceiling 278; headroom 0).
✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files

check:doc-formula-expressions first returned PREREQUISITE NOT MET (@objectstack/formula, then @objectstack/lint, not built). Per the gate's own text — "Nothing was measured … It is NOT a finding" — that was read as NOT MEASURED, not as red; both packages were built and the gate then produced the green above.

Reverse verification

(a) Ratchet ablation past the real headroom. Prediction stated before running: RED. Fix committed first; mutation proven on disk (marker count 1, 278 → 279 lines, blob 7b18c57931b25275); absolute paths in an EXIT INT TERM trap.

RATCHET EXIT UNDER MUTATION = 1
✗ check-skill-line-ratchet: …dispatch-runbook.md is 279 lines; the ratchet ceiling is 278.

Restore proven, not assumed — git checkout HEAD -- (never bare), then blob back to 7b18c579ec2bcaa28284d96d910107873932c7a6, git diff HEAD empty, marker count 0. This also confirms the headroom of 7 was a live ceiling rather than a phantom check.

(b) Anchored-grep acceptance, evaluated against both refs so it is reproducible (git show REF:path), CJK-joined so the wrapped verbatim sentence reassembles:

anchor889ec5b42 (before)0959367f1 (after)
transferable sentence (verbatim)01
NOT MEASURED rule01
applicability domain (适用域)01
negative control (负对照)01
ban on 无零读者键01
file lines271278

Scope

One file, one passage. No changeset — instruction-surface only, no package publishes anything; skip-changeset applied. The surface half of the method (the canonical-diff leg that PR #13108 landed, measured good) is deliberately untouched. Not a duplicate of #12813: that card recorded the method as unwritten; this one records the written method's calibration as unable to authorize readings on generic names.


Generated by Claude Code

…ty domain
The pinned reader-count method is calibrated only on sys_share_link.email_allowlist,
which measures as the population MINIMUM (7 hit lines of 121 keys; median 1366, max
75985). A calibration that passes on the most distinctive key in its population cannot
authorize readings on generic column names, so the duty's 'no zero-reader keys' output
was a false green over 104 unmeasurable keys.
Adds an applicability-domain statement: only keys whose every hit line is adjudicated
produce a reading; all others are recorded NOT MEASURED (neither 0 nor 'has readers').
Adds a generic-name negative control that must read as unadjudicable, so the calibration
can now fail for the reason the duty exists to fear. Output is always two halves.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de
@zhuangjianguo
zhuangjianguo marked this pull request as ready for review August 31, 2026 02:29
@os-zhuang
os-zhuang disabled auto-merge August 31, 2026 02:31
@zhuangjianguoClaude

Copy link
Copy Markdown
CollaboratorAuthor

Standing down — superseded by maintainer ruling; closing this PR unmerged.

Maintainer ruling (2026-08-31, chat, verbatim): 「13580 B(荐」 — option B of the value review offered on this PR: retire duty ⑦'s reader half instead of documenting its limits. The maintainer had already disarmed auto-merge here personally; the ruling settles the direction.

What this means:

  • The +7 methodology lines do not land. The measurement work in this PR stays correct and stays cited on [finding] The standing ⑦ reader-count method is calibrated on the EASIEST key in its population — measured unusable for 104 of 121 columns, so its "no zero-reader keys" is a false green #13304 (population minimum/median/max; the calibration case being the single easiest key; the wrong-direction parenthetical). The ruling is about VALUE, not correctness: rules explaining when a structurally blind instrument may be trusted change no PM decision — removing the instrument does.
  • The reader half of duty ⑦ (the 读者半边 and 正对照 bullets in dispatch-runbook.md) is RETIRED by a net-negative follow-up PR on a fresh branch, quoting the ruling. The surface half (canonical member-level diff, measured good) stays untouched.
  • My contract-review ACCEPT (comment on the source card, 02:23Z) is withdrawn as a landing verdict — its diff verification and budget arithmetic stand as evidence, but the value gate now in force supersedes it.

Branch claude/issue-13304-reader-count-domain is preserved as evidence (the measurements live in its history). No further pushes here.


Generated by Claude Code

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

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@zhuangjianguo@os-zhuang@claude