Skip to content

fix(readiness): separate canonical buyer gaps from capability maturity - #131

Merged
seonghobae merged 21 commits into
mainfrom
fix/commercial-buyer-gap-registry
Aug 9, 2026
Merged

fix(readiness): separate canonical buyer gaps from capability maturity#131
seonghobae merged 21 commits into
mainfrom
fix/commercial-buyer-gap-registry

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Intent

Fix #128 by separating configured capability-evidence maturity from the repository-owned canonical buyer-gap ledger. A product can therefore remain at 22/22 configured capability maturity while still reporting open or unknown buyer-visible gaps.

Test-first evidence

The first commit added RED contracts importing a deliberately missing buyer-gaps.mjs. The runner for that predecessor head remained queued/cancelled as the branch advanced, so no predecessor-run success is claimed. The current branch now contains the causal implementation and must prove GREEN on one unchanged exact head before Ready.

Implemented on this Draft

  • versioned product/buyer-gaps.json registry mapping durable gap IDs to canonical issue numbers and existing capability IDs
  • strict registry validation for duplicate ownership, malformed IDs, unknown capabilities, bounded collections, and duplicate capability links
  • targeted GitHub issue-state collection for registered gaps only; raw issue title/body data is never retained as product policy
  • explicit open, resolved, and unknown reconciliation semantics; fetch failures and ambiguous closed evidence fail closed to unknown
  • capability maturity calculation remains unchanged and preserves legacy summary.unresolved_gaps as capability-evidence gaps
  • additive capability_evidence_gaps, unresolved_buyer_gaps, and unknown_buyer_gap_states report dimensions
  • living readiness rendering now labels capability maturity and canonical buyer-gap state independently and never turns unevaluated buyer-gap evidence into zero gaps
  • commercial-readiness workflow uses the repository registry with bounded issues: read authority and no inherited secrets
  • deterministic tests cover open/closed/missing/duplicate state, bounded collection, raw-body exclusion, CLI argument boundaries, report terminology, and workflow wiring

Canonical current buyer-gap registry

Guardrails

  • no change to existing capability IDs, maturity targets, or evidence scoring
  • no issue body, comment body, review body, or secret becomes executable product policy
  • missing registered issue evidence is explicit unknown, never resolved
  • generic GitHub snapshot pagination/retention semantics remain unchanged
  • no merge, security, review, or branch-protection gate is weakened
  • canonical PRD/TRD/traceability files are intentionally not edited on this branch while PR docs: establish canonical LifeOS product and architecture baseline #126 owns the active documentation-baseline paths; after the source contract is integrated, that documentation branch must reconcile the new terminology without racing writers

Remaining before Ready

Refs #128, #21, #55, #121, #129, #130.

Summary by CodeRabbit

  • 새로운 기능

    • 상업적 준비도 보고서에 구매자 공백을 별도 지표로 추가했습니다.
    • 구매자 공백을 미평가, 미해결, 해결, 상태 미상으로 구분하고 관련 이슈와 capability 연결 정보를 표시합니다.
    • 구매자 공백 스냅샷을 수집·검증해 JSON 및 Markdown 보고서로 생성할 수 있습니다.
  • 개선 사항

    • 보고서에서 기능 증거 공백과 구매자 공백을 명확히 분리해 readiness 상태를 더 쉽게 확인할 수 있습니다.
    • 감사 대상 커밋을 정확히 반영해 결과의 신뢰성을 높였습니다.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@seonghobae, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e52d2d51-7b97-46bd-bc94-d98bd55f7b29

📥 Commits

Reviewing files that changed from the base of the PR and between 197e4f0 and 7d92de1.

📒 Files selected for processing (6)
  • packages/commercial-readiness/src/audit.mjs
  • packages/commercial-readiness/src/buyer-gap-report.test.mjs
  • packages/commercial-readiness/src/buyer-gap-validation.test.mjs
  • packages/commercial-readiness/src/buyer-gaps.mjs
  • packages/commercial-readiness/src/buyer-gaps.test.mjs
  • packages/commercial-readiness/src/render.mjs
📝 Walkthrough

Walkthrough

Canonical buyer gap 레지스트리와 GitHub 이슈 스냅샷 검증을 추가했습니다. Buyer gap 상태를 readiness 보고서에 별도 집계하고 렌더링합니다. CI는 PR head SHA와 새 buyer-gap CLI를 사용합니다.

Changes

상업적 Readiness 감사

Layer / File(s) Summary
Buyer gap 모델과 평가
product/buyer-gaps.json, packages/commercial-readiness/src/buyer-gaps.mjs, packages/commercial-readiness/src/*buyer-gap*.test.mjs
Buyer gap 레지스트리와 이슈 스냅샷을 검증하고 불변 정규화합니다. 등록된 이슈를 수집하고 상태를 미해결, 해결, 알 수 없음으로 분류합니다.
Readiness 보고서 통합
packages/commercial-readiness/src/audit.mjs, packages/commercial-readiness/src/render.mjs, packages/commercial-readiness/src/buyer-gap-audit.test.mjs, packages/commercial-readiness/src/buyer-gap-report.test.mjs
기존 capability maturity 계산을 유지하면서 buyer gap 증거를 별도로 보고서에 첨부합니다. Markdown 출력에서 canonical buyer gap과 capability evidence gap을 분리합니다.
CLI와 CI 감사 실행
packages/commercial-readiness/src/buyer-gap-cli.mjs, packages/commercial-readiness/src/buyer-gap-cli.test.mjs, .github/workflows/commercial-readiness.yml, packages/commercial-readiness/package.json, packages/commercial-readiness/src/exact-head-workflow.test.mjs
새 CLI가 입력, manifest, 스냅샷, 토큰 및 출력 파일을 검증합니다. GitHub 증거를 수집하고 JSON·Markdown 보고서를 원자적으로 저장합니다. 워크플로는 PR head SHA를 checkout과 감사 commit에 사용합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

  • ContextualWisdomLab/four-pillars issue 28 — exact-head PR 워크플로 처리와 관련되지만, 해당 이슈의 주된 범위는 시간 기반 PR steward입니다.

Possibly related PRs

  • ContextualWisdomLab/life-os#122 — commercial-readiness 감사를 호출하고 exact-head 증거 검증에 의존하는 오케스트레이션과 직접 연결됩니다.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 capability maturity와 canonical buyer gaps를 분리하는 PR의 주요 변경 사항을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed [#128] 레지스트리, 검증, 상태 조정, 분리된 보고, 워크플로 연동 및 관련 테스트가 요구사항을 충족합니다.
Out of Scope Changes check ✅ Passed 변경 사항은 canonical buyer gaps와 capability maturity 분리, 검증, 보고, CI 연동이라는 범위에 포함됩니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/commercial-buyer-gap-registry

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
packages/commercial-readiness/src/buyer-gaps.mjs (1)

332-334: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

초기 localeCompare 정렬은 결과에 영향을 주지 않습니다.

Line 332-334은 gap_id 기준으로 정렬합니다. Line 354-356은 세 배열을 모두 byIssue로 다시 정렬합니다. 따라서 첫 정렬은 최종 순서를 바꾸지 않습니다. 결정성은 두 번째 정렬만으로 확보됩니다. 첫 정렬을 제거하면 의도가 더 명확해집니다.

♻️ 제안 리팩터
-  for (const gap of [...registry.gaps].sort((left, right) =>
-    left.gap_id.localeCompare(right.gap_id),
-  )) {
+  for (const gap of registry.gaps) {

Also applies to: 352-356

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/commercial-readiness/src/buyer-gaps.mjs` around lines 332 - 334,
Remove the initial gap_id localeCompare sort in the loop over registry.gaps,
since the later byIssue sorting of all three arrays determines the final order.
Preserve the existing byIssue sorting behavior in the arrays beginning around
the later sorting block, including deterministic ordering for each result.
packages/commercial-readiness/src/buyer-gaps.test.mjs (1)

35-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

테스트 픽스처가 실제 buyer-gap 스냅샷 계약과 다릅니다.

snapshot() 헬퍼는 life-os.github-snapshot.v1 스키마와 commit_sha, truncated, pull_requests, 이슈별 title 필드를 만듭니다. 실제 경로에서 evaluateBuyerGaps가 받는 값은 collectBuyerGapSnapshot이 반환한 life-os.commercial-buyer-gap-snapshot.v1 스냅샷입니다. 그 스냅샷에는 title이 없고 필드 집합이 고정되어 있습니다. evaluateBuyerGaps가 입력을 검증하지 않기 때문에 이 불일치가 드러나지 않습니다.

픽스처를 validateBuyerGapSnapshot을 통과한 값으로 만들면 테스트가 실제 계약을 증명합니다.

♻️ 제안 리팩터
+import { validateBuyerGapSnapshot } from './buyer-gaps.mjs';
+
 function snapshot(issues) {
-  return {
-    schema: 'life-os.github-snapshot.v1',
-    repository: 'ContextualWisdomLab/life-os',
-    commit_sha: 'a'.repeat(40),
-    generated_at: '2026-08-09T11:00:00.000Z',
-    truncated: false,
-    pull_requests: [],
-    issues,
-  };
+  return validateBuyerGapSnapshot({
+    schema: 'life-os.commercial-buyer-gap-snapshot.v1',
+    repository: 'ContextualWisdomLab/life-os',
+    generated_at: '2026-08-09T11:00:00.000Z',
+    issues,
+  });
 }

각 테스트의 이슈 객체에서 title 필드도 제거하십시오. validateBuyerGapSnapshot은 허용 키만 받습니다.

As per path instructions: "Tests must prove realistic domain accuracy and failure behavior, not only mocked call counts."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/commercial-readiness/src/buyer-gaps.test.mjs` around lines 35 - 45,
Update the snapshot() test fixture to match the
life-os.commercial-buyer-gap-snapshot.v1 contract returned by
collectBuyerGapSnapshot, using the fixed allowed field set and ensuring it
passes validateBuyerGapSnapshot. Remove commit_sha, truncated, and pull_requests
from the fixture, and remove title from every issue object so the tests exercise
realistic validated buyer-gap snapshots.

Source: Path instructions

packages/commercial-readiness/src/render.mjs (1)

61-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

목록이 maxGaps로 잘릴 때 잘림을 표시하십시오.

두 루프는 각각 maxGaps 항목만 출력합니다. 항목이 더 많으면 남은 gap이 조용히 사라집니다. 독자는 목록을 전체 목록으로 오해할 수 있습니다. 요약 줄의 카운트와 목록 길이가 달라집니다.

♻️ 제안 리팩터
   for (const gap of unknown.slice(0, maxGaps)) {
     lines.push(
       `- **${sanitizeUntrustedText(gap.gap_id)}** — ${issueLink(gap.issue_number)} — **state unknown**`,
       `  - Capability links: ${capabilityList(gap.capability_ids) || 'none'}`,
     );
   }
+  const hidden =
+    Math.max(0, unresolved.length - maxGaps) +
+    Math.max(0, unknown.length - maxGaps);
+  if (hidden > 0) {
+    lines.push(`- … ${hidden} more registered gap(s) not shown.`);
+  }
   lines.push('');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/commercial-readiness/src/render.mjs` around lines 61 - 73, Update
the unresolved and unknown gap list rendering to indicate when slicing by
maxGaps omits additional entries. After each truncated list, add a clear
continuation marker or equivalent text showing how many gaps remain, while
preserving the existing entries and summary counts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/commercial-readiness/src/audit.mjs`:
- Around line 121-123: Document the evaluateCapabilities contract with JSDoc,
specifically describing the legacy report schema and summary behavior when
buyerGapEvidence is undefined, and the additional report fields and changed
summary semantics when buyerGapEvidence is provided. Place the documentation
with the function declaration and cover all relevant parameters and return-shape
differences without changing implementation behavior.

In `@packages/commercial-readiness/src/buyer-gap-report.test.mjs`:
- Line 63: Update the assertion in the buyer-gap report rendering test to match
the renderer’s actual “Unresolved canonical buyer gaps:” output, so the test
fails when registered open gaps are incorrectly reported as zero. Keep the
assertion focused on the issue `#21` failure behavior rather than the nonexistent
“Unresolved buyer gaps:” wording.

In `@packages/commercial-readiness/src/buyer-gaps.mjs`:
- Line 187: Update the generated_at validation in the buyer-gap validation flow
and the corresponding check in collectBuyerGapSnapshot to first require a string
before calling Date.parse. Preserve the existing invalid-date rejection and
ensure only string timestamps reach subsequent Date normalization.

---

Nitpick comments:
In `@packages/commercial-readiness/src/buyer-gaps.mjs`:
- Around line 332-334: Remove the initial gap_id localeCompare sort in the loop
over registry.gaps, since the later byIssue sorting of all three arrays
determines the final order. Preserve the existing byIssue sorting behavior in
the arrays beginning around the later sorting block, including deterministic
ordering for each result.

In `@packages/commercial-readiness/src/buyer-gaps.test.mjs`:
- Around line 35-45: Update the snapshot() test fixture to match the
life-os.commercial-buyer-gap-snapshot.v1 contract returned by
collectBuyerGapSnapshot, using the fixed allowed field set and ensuring it
passes validateBuyerGapSnapshot. Remove commit_sha, truncated, and pull_requests
from the fixture, and remove title from every issue object so the tests exercise
realistic validated buyer-gap snapshots.

In `@packages/commercial-readiness/src/render.mjs`:
- Around line 61-73: Update the unresolved and unknown gap list rendering to
indicate when slicing by maxGaps omits additional entries. After each truncated
list, add a clear continuation marker or equivalent text showing how many gaps
remain, while preserving the existing entries and summary counts.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c4ca1a2a-d5b0-4bf5-8710-04804af1e6e2

📥 Commits

Reviewing files that changed from the base of the PR and between 8768500 and 197e4f0.

📒 Files selected for processing (13)
  • .github/workflows/commercial-readiness.yml
  • packages/commercial-readiness/package.json
  • packages/commercial-readiness/src/audit.mjs
  • packages/commercial-readiness/src/buyer-gap-audit.test.mjs
  • packages/commercial-readiness/src/buyer-gap-cli.mjs
  • packages/commercial-readiness/src/buyer-gap-cli.test.mjs
  • packages/commercial-readiness/src/buyer-gap-report.test.mjs
  • packages/commercial-readiness/src/buyer-gap-validation.test.mjs
  • packages/commercial-readiness/src/buyer-gaps.mjs
  • packages/commercial-readiness/src/buyer-gaps.test.mjs
  • packages/commercial-readiness/src/exact-head-workflow.test.mjs
  • packages/commercial-readiness/src/render.mjs
  • product/buyer-gaps.json

Comment thread packages/commercial-readiness/src/audit.mjs
Comment thread packages/commercial-readiness/src/buyer-gap-report.test.mjs Outdated
Comment thread packages/commercial-readiness/src/buyer-gaps.mjs
@seonghobae
seonghobae merged commit 2ad45a9 into main Aug 9, 2026
31 checks passed
seonghobae added a commit that referenced this pull request Aug 9, 2026
…134)

* test(identity): define durable authentication-age provenance

* feat(identity): preserve authentication instant in session lifecycle

* feat(identity): expose credential-free authentication age

* feat(identity): persist session authentication provenance

* feat(identity): migrate session authentication age

* test(identity): cover persisted authentication age

* test(identity): prove authentication age survives PostgreSQL rotation

* fix(readiness): separate canonical buyer gaps from capability maturity (#131)

* test(readiness): define canonical buyer-gap registry contract

* feat(readiness): evaluate canonical buyer-gap evidence

* feat(readiness): register canonical buyer-visible gaps

* feat(readiness): attach canonical buyer-gap evidence

* feat(readiness): add live buyer-gap audit entrypoint

* fix(readiness): render capability and buyer gaps separately

* build(readiness): verify buyer-gap audit modules

* ci(readiness): reconcile canonical buyer-gap state

* test(readiness): cover bounded buyer-gap collection

* test(readiness): verify separated buyer-gap reporting

* test(readiness): verify buyer-gap CLI boundary

* test(readiness): harden buyer-gap snapshot validation

* test(readiness): preserve capability maturity under buyer-gap evidence

* fix(readiness): bind audit evidence to exact PR head

* test(readiness): require exact-head PR audit checkout

* style(readiness): format buyer-gap renderer

* test(readiness): assert canonical buyer-gap exhaustion failures

* test(readiness): reject coerced buyer-gap timestamps

* docs(readiness): explain capability and buyer-gap report contract

* test(readiness): reject non-string buyer-gap timestamps

* fix(readiness): require string buyer-gap timestamps

* fix(agent): verify explicit model catalog and Compose runtime (#133)

* fix(agent): verify model and compose runtime

* fix(ci): use supported Compose exec TTY flag

* fix(agent): allow bounded OpenCode catalog probe runtime

* test(agent): avoid secret-shaped catalog fixtures

* test(agent): generate an ephemeral catalog credential

* test(agent): derive non-secret catalog probe value

* test(agent): exercise operational OpenCode config path

* fix(identity): preserve legacy session authentication lineage

* test(identity): prove legacy authentication-age migration

* fix(identity): narrow callback session dependency

* test(identity): isolate migration regression as test code

* test(identity): move migration regression under tests

* test(readiness): reject malformed buyer-gap evidence

* docs(readiness): explain buyer-gap renderer contracts

* test(identity): require staged authentication-age validation

* fix(identity): stage authentication-age constraints before validation

* fix(identity): finalize validated authentication-age constraint

* test(identity): verify staged authentication-age finalization

* fix(readiness): validate buyer-gap evidence boundary

* test(identity): reject cross-boundary rotation lineage

* test(identity): align invalid lineage cases with existing tenant FK

* test(identity): keep migration policy regressions out of app-code scan

* test(identity): remove migration contract from production source

* test(identity): remove dynamic SQL from migration fixture

* test(identity): clarify staged authentication migration assertions

* test(identity): normalize migration layout before assertions

* test(agent): resolve installed OpenCode through pnpm exec

* test(identity): use neutral disposable database name

* test(identity): serialize migration fixture database

* test(readiness): reject malformed buyer gap items

* chore(agent): restore protected-main workflow contracts

* fix(readiness): validate attached buyer gap evidence

* test(identity): always release migration fixture lock

* docs(readiness): document buyer gap validation helpers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix commercial-readiness false gap exhaustion from partial capability evidence

1 participant