feat(psychometric): posterior ESEM input gates with true-parameter RMSE - #49
Conversation
ADR 0005 first production slice: construct classification, refusal of raw-proportion Pearson, CPU f64 OLS and plausible-value loading recovery, invariance-gated mean comparison, and causal-heuristic refusal. No new migration.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough
Changespsychometric_core 구현
Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🟡 Moderate · up to This PR adds posterior ESEM input gates and true-parameter RMSE, but the current head still contains a bypassable invariance assertion, a mismatched uncertainty-coverage threshold, and documentation/ADR claims that do not consistently match the supported scope. These can lead to unsupported scientific interpretations, and exact-head required checks are still incomplete, so merge should wait for fixes or explicit owner acceptance. Sequence Diagram(s)sequenceDiagram
participant Caller
participant IndicatorGate
participant LoadingRecovery
participant RubinCombiner
Caller->>IndicatorGate: IndicatorKind 및 입력 검증
IndicatorGate->>LoadingRecovery: 허용된 factor scores와 indicators 전달
LoadingRecovery-->>RubinCombiner: draw별 OLS loading과 sampling variance 전달
RubinCombiner-->>Caller: mean loading과 total variance 반환
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
…n T and strong means Stacked on #49 psychometric_core. Not a second invariance crate. Not DSEM, RI-CLPM, MGCFA, or Mislevy PVs. - CWC within/between OLS and Kish ESS WLS (Enders & Tofighi, 2007; Curran & Bauer, 2011; Hamaker et al., 2015; Kish, 1965) - Event-time discrete lag-1 and exact scalar log-rate a = ln(φ)/Δt (Voelkle et al., 2012, Eq. 7; Driver et al., 2017, Eq. 3); difference quotient refused - CWC-then-event-time residual lag, still not DSEM - Rubin T_m = Ū_m + (1+1/m) B_m on draw-level OLS loadings (Rubin, 1996, p. 473) - Two-group OLS latent means only under strong/strict; metric/weak is not enough. #84 metric licenses shared metric meaning only.
…re-centering Curran and Bauer (2011, pp. 607–608) show that subtracting the person-specific mean from a raw autoregressive series does not isolate the lagged within-person effect. Caller-supplied residuals with irregular event intervals recover a = ln(φ)/Δt. Still not DSEM. Meredith (1993) and Mislevy (1991) remain unread (Unpaywall closed).
* docs: add live product technical gap baseline * docs: refresh live delivery baseline * docs: record psychometric draft state * docs: refresh temporal and image delivery queue * docs: record Unicode identity PR in gap baseline * docs: refresh buyer gap baseline for psychometric PR * docs: refresh baseline for branch coverage repair * docs: refresh baseline with current PR heads * docs: record validation claim promotion head * docs: refresh live PR baseline * docs: bind baseline to refresh head * docs: refresh gap baseline current heads * docs: refresh live pull request evidence * docs: expand TEPP completion gaps and issue roadmap * docs: add product and technical gap baseline * docs: normalize buyer gap maturity * docs: remove duplicate gap baseline index entry * docs(gap): refresh live queue snapshot * docs(gap): split executable product capabilities * docs(gap): align queue snapshot timestamp * docs(gap): normalize maturity and authority fields * docs: refresh live pull request gap evidence * docs: record hot partition readiness gap * docs: refresh live product gap snapshot * docs: route Strix gap evidence to canonical owner * docs: link exact Strix failure records * docs: bound Strix evidence to public records * docs(gaps): refresh live PR snapshot * docs(gaps): bind snapshot to refreshed head * docs: refresh live buyer-gap baseline * docs: refresh live PR head register * docs: require dated exact-head gap baseline Refresh the live 2026-08-23 GitHub inventory, add a structure validator that rejects queued-Checks-as-shipped claims, and cover it with quality tests. PR #164 remains the single gap-baseline authority. * docs: drop closed duplicate #179 from live register Refresh the exact-head inventory to 93 open PRs after closing the duplicate gap-baseline snapshot. PR #164 remains the single authority. * docs: bind gap heads to the live 95-PR register Refresh the exact-head inventory to 2026-08-23T13:31:34Z and set each buyer-gap current head SHA to that snapshot, including #164, #48, #50, #63, and #157. Queued Checks remain not implemented-main. * fix(docs): honor negated queued-Checks wording in the gap validator The promotion guard treated any same-line 'queued Checks'...'implemented-main' window as a shipped claim, so correct negation failed and a wrapped affirmative claim could evade. Require an un-negated match, including across a line break. * docs: refresh 96-PR exact-head gap register Bind the live 2026-08-23T13:57:34Z inventory, including #49 extra-lag underflow coverage and hourly #180/#164 repairs. Buyer-gap current heads match that snapshot. Queued Checks are not implemented-main. * docs: replace Buyer language in the live gap register TEPP is not a purchase catalog. The gap baseline, validator, and CHANGELOG now name operator-visible gaps and operator-gap closure evidence. * docs: refresh 96-PR exact-head gap register after #49 90b08bb Bind GAP current-head SHA prefixes to the 2026-08-23T14:08:41Z live inventory, including #49 extra-process lag underflow and #164 8b78797. Queued Checks remain not implemented-main. * docs: refresh 98-PR exact-head operator-gap register Snapshot 2026-08-23T14:52:16Z. GAP-015 records this register's prior exact head 33bf824. TEPP remains an operator-gap register, not a purchase catalog. * docs: refresh 97-PR exact-head gap register after stacked #191 Drop closed stacked #191, bind GAP current-head SHAs to the 2026-08-23T15:07:33Z REST inventory, and keep queued Checks off implemented-main. * docs: refresh 97-PR exact-head gap register after #49 1e3e2eb Bind GAP current-head SHAs to the 2026-08-23T15:57:48Z REST inventory, including #49 exp/expm1 overflow rewrites. Queued Checks remain off implemented-main. * docs: refresh 99-PR exact-head gap register after stacked #193/#194 Bind GAP current-head SHAs to the 2026-08-23T16:52:27Z REST inventory. #49 nightly 1701/1702 on 1e3e2eb is logged, not treated as implemented-main. Queued Checks remain off protected-main. * docs: refresh 101-PR exact-head gap register after #49 dd6cf43 Bind GAP current-head SHAs to the 2026-08-23T17:59:26Z REST inventory, including unique-fold coverage on #49 and stacked drafts #195/#196. Queued Checks remain off implemented-main. * fix(docs): require promotion-denial wording for queued-Checks guard An unrelated `not` in `queued Checks are not required; this PR is implemented-main` was treated as negation. Only never/do not/does not/ cannot/must not plus promote/treat/make/mean licenses the span. * docs: refresh 103-PR exact-head gap register after stacked #197/#198 Bind GAP current-head SHAs to the 2026-08-23T18:48:36Z REST inventory. #49 unique-fold nightly is green on dd6cf43 and is not implemented-main. Queued Checks remain off protected-main. * docs: refresh 105-PR exact-head gap register after stacked #199/#200 Bind GAP current-head SHAs to the 2026-08-23T19:48:22Z REST inventory. #49 unique-fold nightly remains green on dd6cf43 and is not implemented-main. Queued Checks remain off protected-main. * docs: refresh 106-PR exact-head gap register after #201 span units Bind GAP-005 to #201 5344729 as a first span-unit slice only, not concept alignment and not implemented-main. Queued Checks remain off protected-main. * docs: refresh 109-PR exact-head gap register after #201 6afd650 Bind GAP-005 to the RFC 5646-once citation head and include stacked drafts #202–#204. Queued Checks remain off implemented-main. * fix(docs): deny queued-Checks promotion by governed negation Replace the fixed-window denial regex with sentence-scoped negation governance: a claim passes only when a negation cue directly governs a promotion verb inside the same sentence and no adversative conjunction separates that pair from the maturity assertion. Honest wordings whose negation precedes the phrase are accepted, while unrelated negated clauses joined by 'but' can no longer license an implemented-main claim. * docs: refresh 117-PR exact-head gap register after #205-#212 Eight new draft PRs opened in the Driver-recovery psychometric series since the last snapshot. Protected-main SHA and non-draft count (70) are unchanged -- no new mergeable work landed, this is a pure register refresh. * docs: refresh live product gap evidence * docs(changelog): align gap-baseline snapshot entry with committed register The Changed entry still described the predecessor 2026-08-23T21:46:37Z snapshot (109 open / 39 drafts) while the committed baseline document records the refreshed 2026-08-24T05:41:54Z snapshot (118 open / 48 drafts). Align the changelog narrative with the register the validator enforces so the exact-head evidence stays internally consistent. --------- Co-authored-by: seonghobae <seonghobae@users.noreply.github.com> Co-authored-by: opencode-agent <agent@contextualwisdomlab.dev>
| return merged | ||
|
|
||
|
|
||
| def load_union_branch_totals(files: Sequence[object]) -> Mapping[str, int | float]: |
| def load_union_branch_totals(files: Sequence[object]) -> Mapping[str, int | float]: | ||
| """Merge LLVM branch outcomes by source coordinate across test binaries.""" | ||
|
|
||
| outcomes: dict[tuple[str, int, int, int, int], list[int]] = {} | ||
| for file_record in files: | ||
| if not isinstance(file_record, Mapping): | ||
| raise ValueError("coverage file record must be an object") | ||
| filename = file_record.get("filename") | ||
| if "branches" not in file_record: | ||
| raise ValueError("coverage file record must contain branches") | ||
| branches = file_record["branches"] | ||
| if not isinstance(filename, str) or not filename: | ||
| raise ValueError("coverage file record must contain a filename") | ||
| if not isinstance(branches, list): | ||
| raise ValueError("coverage branches must be a list") | ||
| for branch in branches: | ||
| if not isinstance(branch, list) or len(branch) < 6: | ||
| raise ValueError("coverage branch record is malformed") | ||
| coordinates = branch[:4] | ||
| counts = branch[4:6] | ||
| if not all( | ||
| isinstance(value, int) and not isinstance(value, bool) and value >= 0 | ||
| for value in coordinates | ||
| ): | ||
| raise ValueError("coverage branch coordinates are invalid") | ||
| if not all( | ||
| isinstance(value, int) and not isinstance(value, bool) and value >= 0 | ||
| for value in counts | ||
| ): | ||
| raise ValueError("coverage branch counts are invalid") | ||
| key = (filename, *coordinates) | ||
| outcome = outcomes.setdefault(key, [0, 0]) | ||
| outcome[0] += counts[0] | ||
| outcome[1] += counts[1] | ||
| count = len(outcomes) * 2 | ||
| covered = sum(outcome > 0 for counts in outcomes.values() for outcome in counts) | ||
| return {"count": count, "covered": covered} |
There was a problem hiding this comment.
🟡 Duplicate coverage helper left after incomplete refactor
load_union_branch_totals is defined twice, identically, in check_coverage.py and check_coverage.py. The rewrite pointed load_totals at the new fold_unique_branch_totals, so the first copy is dead code shadowed by the second. Behavior is unchanged because the bodies match, but the redundant definition should be removed.
Prompt for agents
The function load_union_branch_totals is now defined twice with identical bodies in scripts/check_coverage.py (around lines 40-76 and again around lines 152-188). Remove one of the two duplicate definitions, keeping a single canonical definition. Confirm the remaining definition is still the one referenced by tests/quality/test_check_coverage.py (coverage_contract.load_union_branch_totals). Note that load_totals no longer calls this helper (it uses fold_unique_branch_totals), so verify the helper is only needed for the test path before deciding placement.
Was this helpful? React with 👍 or 👎 to provide feedback.
# Conflicts: # Cargo.toml # README.md # docs/TRACEABILITY.md # docs/adr/README.md # docs/research/standards-and-literature.md
The #49 merge left two load_union_branch_totals definitions, so the first body was dead and the live success path had no test. Keep one helper and cover valid multi-file True/False unions. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Summary
ADR 0005 first executable slice. No new migration (
0007remains owned by #45).psychometric_coreclassifies constructs asreflective/formative/network/unresolvedand refuses fit-driven reinterpretation as reflective.f64OLS recovers a known reflective loading; plausible-value averaging of posterior indicator draws reduces computed RMSE relative to a single draw (RMSE is computed, not hard-coded).topic_measurementremains feat(topic): logistic-normal ALR coordinates with true-parameter RMSE #48).Test plan
esem_input_recovery_contractfailed to compile (E0432) before the public API existedcargo test -p psychometric_core --offline(lib + contract tests)cargo clippy --workspace --all-targets --offline -- -D warningspython3 scripts/validate_documentation.py,check_docstrings.py,check_workspace_contract.pycargo test --workspace --offline --lib --testsDo not self-approve or merge until exact-head required checks and a qualifying independent review pass. Prefer merging #37 then #45 when those become eligible.
Summary by CodeRabbit
새 기능
문서
품질 개선