Skip to content

feat(identity): preserve authentication age across session rotation - #134

Merged
seonghobae merged 37 commits into
mainfrom
feat/data-rights-authentication-age
Aug 9, 2026
Merged

feat(identity): preserve authentication age across session rotation#134
seonghobae merged 37 commits into
mainfrom
feat/data-rights-authentication-age

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Buyer-visible outcome

Advance #55's authenticated/recent-auth data-rights boundary without resurrecting the superseded parallel data-rights-service: preserve the actual authentication instant independently from ordinary session rotation so a future export/deletion HTTP boundary can enforce recent re-authentication honestly.

Test-first evidence

The implementation contract requires:

  • a newly authenticated session to expose an explicit authenticatedAt instant;
  • browser session introspection to expose that credential-free provenance;
  • session rotation to create a new createdAt while preserving the original authenticatedAt rather than falsely treating rotation as re-authentication;
  • a legacy rotated-session chain to migrate without making child rotation time look like fresh authentication.

Implemented on the current branch

  • SessionRecord and ActiveSession carry authenticatedAt separately from session issuance time;
  • fresh authentication sets authenticatedAt to the authentication instant;
  • ordinary session rotation preserves the original authentication instant while rotating session identity/token and createdAt;
  • browser session introspection exposes the credential-free authentication instant;
  • PostgreSQL session persistence reads/writes authenticated_at explicitly;
  • migration 0004_session_authentication_age.sql reconstructs legacy authentication provenance through the retained rotated_from_id chain, carries each root session's creation instant forward to its descendants, refuses cross-user/workspace lineage, and stages authentication constraints with NOT VALID;
  • migration 0005_finalize_session_authentication_age.sql validates the staged constraints before the short final SET NOT NULL transition;
  • real PostgreSQL migration regressions under the package tests/ boundary use one fixed static disposable database name, apply the legacy schema, verify a three-session rotation chain, and require corrupt cross-user/workspace lineage to fail closed;
  • migration-contract tests normalize SQL layout whitespace before asserting the staged/finalized constraint sequence;
  • the OAuth callback boundary depends only on the issued-session field it actually consumes (expiresAt) rather than coupling callback tests to every internal session metadata field;
  • unit and PostgreSQL integration contracts cover persistence and rotation provenance, including later rotation times;
  • current protected main, including fix(readiness): separate canonical buyer gaps from capability maturity #131 and fix(agent): verify explicit model catalog and Compose runtime #133, is integrated into the branch without changing this feature's ownership boundary;
  • the inherited OpenCode catalog contract now resolves the actually installed binary through the workspace's pnpm --filter ... exec opencode path rather than inferring a platform package directory;
  • no deletion/export route or arbitrary recent-auth threshold is introduced in this bounded prerequisite slice.

Security and migration boundary

  • identity service remains the sole owner of session authentication provenance;
  • no provider credential, workspace authority, session TTL, or cross-service database contract is widened;
  • legacy rows are not independently backfilled from each row's created_at; descendants inherit the authenticated chain root and unresolved/corrupt lineage fails the migration rather than fabricating a recent authentication instant;
  • the migration integration fixture no longer constructs database identifiers dynamically; the disposable database identifier is a fixed test-only SQL literal;
  • current AppGuardrail/security evidence must pass on the exact unchanged head before merge;
  • future destructive data-rights routes must define their reviewed maximum authentication age separately rather than infer one here.

Remaining before merge

  • exact-current-head focused identity tests, real PostgreSQL migration/repository integration, typecheck/build/root formatting, CI, AppGuardrail, Semgrep, Security Scan, Commercial Readiness and CodeRabbit/review evidence must pass on one stable head;
  • every current actionable review/security finding must be resolved on that same exact head; stale predecessor findings do not transfer;
  • canonical docs PR docs: establish canonical LifeOS product and architecture baseline #126 must eventually record this implementation as Implemented on active PR until protected integration;
  • ancestry and merge gates must be rechecked against the live protected-main tip immediately before merge.

Refs #55.

Summary by CodeRabbit

  • 새로운 기능

    • 상용 준비성 보고서에 구매자 요구사항 격차와 해결 상태가 추가되었습니다.
    • 구매자 격차를 검증하고 관련 증거를 자동 수집·보고할 수 있습니다.
    • 세션에 최초 인증 시각이 기록되어 갱신 후에도 인증 연령을 추적합니다.
  • 보안 및 안정성 개선

    • 세션 인증 정보의 데이터 무결성 검증이 강화되었습니다.
    • PostgreSQL 및 NATS 환경의 상태 확인과 정리가 자동화되었습니다.
    • 인프라 포트가 루프백으로 제한되고 이미지 버전이 고정되었습니다.
    • 모델 및 Compose 검증 절차가 강화되었습니다.
  • 문서

    • 운영 및 상용 개발 검증 절차가 업데이트되었습니다.

@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: 54 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: 59e8b1da-4c03-4077-a63e-f82ed2dc73bc

📥 Commits

Reviewing files that changed from the base of the PR and between e2ae810 and 5a2f400.

📒 Files selected for processing (2)
  • apps/identity-service/tests/session-authentication-migration.integration.test.ts
  • packages/commercial-readiness/src/buyer-gaps.mjs
📝 Walkthrough

Walkthrough

세션 인증 시각을 데이터베이스와 세션 흐름에 연결했습니다. Buyer-gap 레지스트리와 GitHub 증거 감사 기능을 추가했습니다. OpenCode 모델 검증, Compose 런타임 검증, CI 계약을 강화했습니다.

Changes

세션 인증 연령

Layer / File(s) Summary
인증 시각 스키마
apps/identity-service/migrations/*, apps/identity-service/tests/session-authentication-migration.*
authenticated_at 컬럼과 단계별 제약 조건을 추가했습니다. 기존 세션 회전 계보의 인증 시각을 채우고 마이그레이션 순서를 검증합니다.
세션 인증 시각 흐름
apps/identity-service/src/auth-security.ts, apps/identity-service/src/oauth-*
세션 타입, 발급, 회전, OAuth 반환 및 HTTP 뷰에 authenticatedAt을 연결했습니다. 회전 후에도 최초 인증 시각을 유지합니다.
세션 저장 및 회전 검증
apps/identity-service/src/postgres-security-repositories*, apps/identity-service/src/authentication-age.test.ts
PostgreSQL 저장소가 인증 시각을 저장하고 조회합니다. 단위 및 통합 테스트가 인증 시각 보존과 새 발급 시각을 검증합니다.

Buyer-gap 상용 준비성 감사

Layer / File(s) Summary
Buyer-gap 계약 및 평가
product/buyer-gaps.json, packages/commercial-readiness/src/buyer-gaps.*, packages/commercial-readiness/src/buyer-gap-validation.test.mjs
Buyer-gap 레지스트리와 스냅샷을 검증합니다. GitHub 이슈 증거를 unresolved, resolved, unknown으로 평가하고 보고서에 첨부합니다.
Buyer-gap 감사 CLI
packages/commercial-readiness/src/buyer-gap-cli.*, packages/commercial-readiness/package.json
CLI 인자와 JSON 입력을 검증합니다. GitHub 증거를 수집하고 JSON 및 Markdown 결과를 원자적으로 기록합니다.
보고서 및 워크플로 통합
packages/commercial-readiness/src/audit.mjs, packages/commercial-readiness/src/render.mjs, .github/workflows/commercial-readiness.yml, packages/commercial-readiness/src/*test.mjs
Capability evidence gap과 canonical buyer-gap을 별도 지표로 렌더링합니다. 워크플로는 PR head SHA와 새 감사 CLI를 사용합니다.

OpenCode 및 Compose 검증

Layer / File(s) Summary
OpenCode 모델 계약
.github/workflows/opencode-commercial-development.yml, ARCHITECTURE.md, CHANGELOG.md, docs/operations/*, packages/commercial-development-agent/src/workflow-contract.test.mjs
검토된 NVIDIA 모델을 명시적으로 등록하고 오프라인 카탈로그와 whitelist를 검증합니다. 프로젝트 설정 탐색과 자동 모델 조회를 차단합니다.
모델 카탈로그 및 검증 게이트
.github/workflows/opencode-commercial-development.yml, docs/research/*, docs/superpowers/*
모델 카탈로그 검증을 credential bridge의 선행 조건으로 설정합니다. source 검증과 Compose 설정 검증을 분리하고 결과를 영수증에 기록합니다.
Compose 런타임 CI
compose.yaml, .github/workflows/ci.yml, docs/operations/opencode-commercial-development-loop.md
PostgreSQL 및 NATS 이미지를 digest로 고정하고 loopback 포트 바인딩을 사용합니다. CI가 서비스를 기동하고 상태를 확인한 뒤 실패 진단과 정리를 수행합니다.
워크플로 계약 검증
packages/commercial-development-agent/src/workflow-contract.test.mjs, packages/commercial-development-agent/vitest.config.mjs
모델 격리, Compose 권한 분리, CI 런타임 프로브, 영수증 및 게시 계약을 검증합니다. 테스트 제한 시간을 설정합니다.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PullRequestCI
  participant OpenCodeWorkflow
  participant ModelCatalog
  participant Compose
  participant CommercialReadiness
  PullRequestCI->>Compose: PostgreSQL 및 NATS 기동과 상태 확인
  OpenCodeWorkflow->>ModelCatalog: 오프라인 NVIDIA 모델 검증
  OpenCodeWorkflow->>Compose: Compose 설정 검증
  OpenCodeWorkflow->>PullRequestCI: 검증 영수증 생성
  CommercialReadiness->>CommercialReadiness: Buyer-gap 감사 실행
  CommercialReadiness-->>PullRequestCI: JSON 및 Markdown 보고서 기록
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.65% 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
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 세션 순환 중 인증 시각을 보존하는 이 PR의 주요 identity 변경을 정확하고 간결하게 설명합니다.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/data-rights-authentication-age

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.

Copy link
Copy Markdown
Contributor Author

@opencode-agent Implement PR #134 directly on exact head 35fd24c396aca6f43242be661b4965c718e7dc76 only. The existing test-only commit is intentionally RED and defines the causal contract for #55's future recent-auth data-rights boundary.

Implement the narrowest identity-owned GREEN path without reviving the superseded parallel data-rights-service:

  1. Extend SessionRecord and ActiveSession with authenticatedAt as a canonical UTC instant representing the last provider authentication, distinct from ordinary session issuance/rotation.
  2. SessionService.create() sets authenticatedAt to the authentication instant. rotate() creates a new session with a new createdAt/expiry but MUST preserve the prior authenticatedAt; rotation must never masquerade as re-authentication.
  3. Add the next ordered identity migration to persist authenticated_at timestamptz NOT NULL, backfill existing rows from created_at, and constrain it so it cannot be later than the session created_at. Preserve UUIDv4, tenant ownership, revocation, and rotation constraints.
  4. Update PostgresSessionRepository row mapping, SELECT/INSERT contracts, focused repository unit tests, and real PostgreSQL integration evidence so round-trip/rotation preserves the authentication instant and rejects malformed stored timestamps fail-closed.
  5. Include authenticatedAt in the credential-free /v1/session introspection view and update the exact HTTP/boundary tests. Do not expose session tokens or provider credentials.
  6. Do not invent or hard-code a recent-auth threshold and do not expose export/deletion endpoints yet; this slice only establishes truthful authentication-age provenance for the next reviewed Add complete tenant export and deletion orchestration #55 slice.
  7. Keep changes inside the identity-owned session/migration/tests/docs boundary. Update scoped data-rights/identity documentation or CHANGELOG only where needed to state the new prerequisite honestly.

Run the new RED test first and record that it fails for the expected missing field, then implement and run identity unit tests, PostgreSQL integration with all ordered migrations, typecheck/build/root format, and any exact relevant coverage/docstring gates. If the PR head is no longer exactly the expected head before writing, stop instead of racing another writer. Commit the bounded implementation to this branch; keep the PR Draft until exact-head CI/security/review evidence is green.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

One migration/security condition should be part of the GREEN contract before this Draft becomes Ready: do not backfill authenticated_at from the current session row's created_at unless provenance proves that row was created by authentication rather than rotation. On protected main, rotation issues a new session with a new createdAt, so authenticated_at = created_at can make an already-old login appear freshly authenticated after migration. Prefer fail-closed unknown/null provenance for legacy rows, or reconstruct the root authentication instant from the retained rotation chain and prove it with a PostgreSQL migration regression. Add a realistic pre-migration rotated-session fixture and assert the migrated session can never gain a more recent authentication instant than its actual chain root. This matters because #55 will later use this field for high-impact export/deletion recent-auth decisions.

#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 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
@seonghobae
seonghobae marked this pull request as ready for review August 9, 2026 15:06

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/identity-service/src/auth-security.ts (1)

208-224: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

세션 계약에 설명 docstring을 추가하십시오.

SessionRecordActiveSession은 production declaration이지만 설명 docstring이 없습니다. authenticatedAt이 최초 인증 시각이며 회전 후에도 유지되는 UTC 값임을 계약에 기록하십시오.

As per coding guidelines, “Production declarations must include explanatory docstrings sufficient for a new contributor to understand the contract without reconstructing the implementation.”

🤖 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 `@apps/identity-service/src/auth-security.ts` around lines 208 - 224, Update
the SessionRecord and ActiveSession interface declarations with explanatory
docstrings documenting that authenticatedAt is the UTC timestamp of the initial
authentication and remains unchanged after session rotation. Keep the contract
clear for new contributors and preserve the existing field definitions.

Source: Coding guidelines

🧹 Nitpick comments (11)
packages/commercial-readiness/src/buyer-gap-audit.test.mjs (2)

8-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

픽스처를 validateCapabilityManifest로 통과시키십시오.

manifest 상수는 원시 객체이며 schema 키가 없습니다. evaluateCapabilitiesmanifest.capabilities만 읽으므로 지금은 동작합니다. 그러나 이 픽스처는 실제 검증된 매니페스트 형태와 다릅니다. audit.test.mjsmanifest() 헬퍼처럼 검증기를 통과시키면 픽스처가 실제 계약과 계속 일치합니다.

🤖 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-gap-audit.test.mjs` around lines 8 -
31, Update the manifest fixture to pass through validateCapabilityManifest,
following the pattern used by the manifest() helper in audit.test.mjs, so it
includes the validated schema and matches the real manifest contract before
evaluateCapabilities consumes it.

43-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

레거시 형태의 단언과 unknown/resolved 사례를 추가하십시오.

테스트 이름은 "byte-for-byte equivalent"를 주장하지만, 단언은 capabilities, gaps, 두 개의 요약 값만 비교합니다. 핵심 하위 호환 계약, 즉 buyerGapEvidence가 없을 때 신규 필드가 추가되지 않는다는 점이 검증되지 않습니다.

또한 resolvedunknown이 모두 빈 배열입니다. 따라서 buyer_gap_resolvedunknown_buyer_gap_states의 비영(非零) 경로가 검증되지 않습니다.

💚 제안 추가 단언
     assert.deepEqual(enriched.capabilities, legacy.capabilities);
     assert.deepEqual(enriched.gaps, legacy.gaps);
+    assert.equal(Object.hasOwn(legacy, 'buyer_gaps'), false);
+    assert.equal(Object.hasOwn(legacy, 'buyer_gap_unknown'), false);
+    assert.equal(Object.hasOwn(legacy, 'buyer_gap_resolved'), false);
+    assert.equal(Object.hasOwn(legacy.summary, 'capability_evidence_gaps'), false);
     assert.equal(
       enriched.summary.weighted_maturity_percent,
       legacy.summary.weighted_maturity_percent,
     );
     assert.equal(enriched.summary.unresolved_gaps, legacy.summary.unresolved_gaps);
     assert.equal(enriched.summary.capability_evidence_gaps, 0);
     assert.equal(enriched.summary.unresolved_buyer_gaps, 1);
     assert.equal(enriched.summary.unknown_buyer_gap_states, 0);
+    assert.deepEqual(enriched.buyer_gaps, [
+      {
+        gap_id: 'today.multi-device-sync',
+        issue_number: 121,
+        capability_ids: ['planning.durable-data'],
+        state: 'open',
+        resolution: null,
+      },
+    ]);
   });
+
+  it('reports non-zero unknown and resolved buyer-gap dimensions', async () => {
+    const rootDir = await mkdtemp(join(tmpdir(), 'life-os-buyer-gap-audit-'));
+    await writeFile(join(rootDir, 'evidence.txt'), 'durable', 'utf8');
+
+    const report = await evaluate(rootDir, {
+      unresolved: [],
+      resolved: [
+        {
+          gap_id: 'data.portability-completion',
+          issue_number: 55,
+          capability_ids: ['planning.durable-data'],
+          state: 'closed',
+          resolution: 'completed',
+        },
+      ],
+      unknown: [
+        {
+          gap_id: 'plugins.runtime-delivery',
+          issue_number: 130,
+          capability_ids: ['planning.durable-data'],
+          state: 'unknown',
+          resolution: null,
+        },
+      ],
+    });
+
+    assert.equal(report.summary.unresolved_buyer_gaps, 0);
+    assert.equal(report.summary.unknown_buyer_gap_states, 1);
+    assert.equal(report.buyer_gap_resolved.length, 1);
+    assert.equal(report.buyer_gap_unknown.length, 1);
+  });
🤖 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-gap-audit.test.mjs` around lines 43 -
72, Extend the test around evaluate to verify the legacy result shape when
buyerGapEvidence is undefined: assert the complete returned structure is
byte-for-byte equivalent or that no new buyer-gap fields are present, while
retaining the existing maturity comparisons. Populate the configured evidence
with representative resolved and unknown entries, then assert buyer_gap_resolved
and unknown_buyer_gap_states report the expected nonzero counts alongside the
existing unresolved count.
packages/commercial-readiness/package.json (1)

7-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

파일 목록 중복을 줄이고, build의 누락 항목을 확인하십시오.

세 스크립트가 거의 같은 파일 목록을 반복합니다. 신규 파일 두 개를 추가하면서 중복이 커졌습니다. node --check src/*.mjs 같은 단일 글로브로 대체하면 파일 추가 시 세 곳을 수정할 필요가 없습니다.

또한 buildschema.mjs, audit.mjs, pr-gate.mjs, render.mjs를 검사하지 않습니다. 이는 linttypecheck와 다릅니다. 의도한 차이인지 확인하십시오.

♻️ 제안 변경
-    "build": "node --check src/cli.mjs && node --check src/github-client.mjs && node --check src/buyer-gaps.mjs && node --check src/buyer-gap-cli.mjs",
-    "lint": "node --check src/schema.mjs && node --check src/audit.mjs && node --check src/pr-gate.mjs && node --check src/render.mjs && node --check src/github-client.mjs && node --check src/cli.mjs && node --check src/buyer-gaps.mjs && node --check src/buyer-gap-cli.mjs",
+    "build": "npm run lint",
+    "lint": "for file in src/*.mjs; do node --check \"$file\" || exit 1; done",
     "test": "node --test src/*.test.mjs",
-    "typecheck": "node --check src/schema.mjs && node --check src/audit.mjs && node --check src/pr-gate.mjs && node --check src/render.mjs && node --check src/github-client.mjs && node --check src/cli.mjs && node --check src/buyer-gaps.mjs && node --check src/buyer-gap-cli.mjs"
+    "typecheck": "npm run lint"
🤖 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/package.json` around lines 7 - 10,
package.json의 build, lint, typecheck 스크립트에서 반복된 파일 목록을 node --check src/*.mjs 단일
글로브로 통일하고, build에도 schema.mjs, audit.mjs, pr-gate.mjs, render.mjs 검사를 포함하십시오.
packages/commercial-readiness/src/buyer-gap-cli.mjs (2)

86-99: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

토큰 검증을 파일 읽기보다 먼저 수행하십시오.

GITHUB_TOKEN이 없으면 이 함수는 네 개 JSON 파일을 읽고 검증한 뒤에 실패합니다. 필수 자격 증명이 없으면 즉시 실패하는 편이 진단에 유리합니다. 토큰 검사를 함수 시작으로 옮기십시오.

🤖 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-gap-cli.mjs` around lines 86 - 99,
Move the GITHUB_TOKEN validation in the CLI entry function ahead of the
Promise.all JSON reads and subsequent manifest, registry, snapshot, and policy
validation. Preserve the existing required-token condition and error behavior,
but fail immediately before any file I/O.

76-82: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

rename 실패 시 임시 파일을 제거하십시오.

writeFile이 성공하고 rename이 실패하면 .tmp 파일이 출력 디렉터리에 남습니다. 워크플로가 재실행될 때마다 잔여 파일이 누적됩니다. 실패 경로에서 임시 파일을 삭제하십시오.

♻️ 제안 수정
+import { lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
   const temporary = `${target}.${randomUUID()}.tmp`;
-  await writeFile(temporary, content, { encoding: 'utf8', mode: 0o600 });
-  await rename(temporary, target);
+  try {
+    await writeFile(temporary, content, { encoding: 'utf8', mode: 0o600 });
+    await rename(temporary, target);
+  } catch (error) {
+    await rm(temporary, { force: true });
+    throw error;
+  }
 }
🤖 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-gap-cli.mjs` around lines 76 - 82,
Update writeAtomic so a failed rename removes the temporary file created by
writeFile. Wrap the rename operation in failure handling, clean up temporary on
error, and preserve the original rename failure after cleanup.
packages/commercial-readiness/src/buyer-gaps.mjs (1)

342-350: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

중복 조건을 제거하십시오.

issue.state === 'closed' 비교는 항상 참입니다. 앞의 두 분기가 unknownopen을 이미 처리합니다. 조건을 단순화하면 의도가 명확해집니다.

♻️ 제안 변경
     const resolution = resolutionFor(issue);
-    if (issue.state === 'closed' && resolution !== null) {
+    if (resolution !== null) {
       resolved.push(gapEvidence(gap, 'closed', resolution));
       continue;
     }
🤖 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 342 - 350, In
the issue-state handling around resolutionFor, remove the redundant issue.state
=== 'closed' check and rely on the prior unknown/open branches to reach this
path. Keep the resolution !== null guard and existing resolved evidence behavior
unchanged.
packages/commercial-readiness/src/buyer-gap-cli.test.mjs (1)

1-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

파일 경계의 실패 동작 테스트를 추가하십시오.

이 파일은 parseBuyerGapArguments만 검증합니다. readJsonwriteAtomic의 실패 동작은 검증되지 않습니다. 두 함수는 보안 및 무결성 경계를 담당합니다. 다음 사례를 추가하십시오.

  • 심볼릭 링크 입력 거부: 'Buyer gap audit input must be a regular file'.
  • 크기 제한 초과 입력 거부: 'Buyer gap audit input exceeded the size limit'.
  • 잘못된 JSON 거부: 'Buyer gap audit JSON was invalid'.
  • GITHUB_TOKEN이 비어 있을 때 runBuyerGapAudit 실패: 'GitHub token is required'.
  • writeAtomic 결과 파일의 모드가 0o600인지 확인.

파서 거부 사례도 두 가지가 빠져 있습니다. 값이 --로 시작하는 경우와 값 길이가 500자를 초과하는 경우입니다.

As per coding guidelines: "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-gap-cli.test.mjs` around lines 1 -
53, Extend buyer-gap CLI tests beyond parseBuyerGapArguments to cover readJson
and writeAtomic security and integrity failures: reject symlink inputs,
oversized inputs, and invalid JSON with the specified messages, verify
runBuyerGapAudit rejects an empty GITHUB_TOKEN, and assert writeAtomic output
files use mode 0o600. Also add parser rejection cases for option values
beginning with -- and values exceeding 500 characters, preserving the existing
invalid-command error behavior.

Source: Coding guidelines

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

234-237: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

신규 buyer-gap 필드 부재로 기존 소비자가 안전함을 문서에 명시하십시오.

renderCommercialReadinessIssuesummary.unresolved_buyer_gaps가 없을 때 buyer-gap evidence를 “not evaluated”로 처리하지만, schema 자체는 legacy 형식과 buyer-gap evidence가 추가된 형태 모두 life-os.commercial-readiness-report.v1입니다. 워크플로와 design 문서에서 저장된 보고서를 읽을 때 summary의 buyer-gap 필드가 없는 legacy 형태도 허용한다는 점을 명시하십시오.

🤖 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/audit.mjs` around lines 234 - 237, Update
the workflow and design documentation for renderCommercialReadinessIssue to
explicitly state that stored life-os.commercial-readiness-report.v1 reports
remain compatible when summary.unresolved_buyer_gaps is absent. Document that
this legacy form is treated as buyer-gap evidence “not evaluated,” while reports
containing the new buyer-gap field continue to be supported.
packages/commercial-development-agent/vitest.config.mjs (1)

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

전역 타임아웃 대신 해당 테스트에만 타임아웃을 지정하십시오.

이 값은 패키지의 모든 테스트에 적용됩니다. 느린 테스트는 OpenCode 바이너리를 실행하는 하나뿐이며, 그 테스트는 이미 spawnSynctimeout: 30_000을 지정합니다. 전역 45초 설정은 나머지 결정적 테스트에서 발생하는 정지를 늦게 드러냅니다. Vitest는 세 번째 인자로 테스트별 타임아웃을 받습니다.

♻️ 제안 수정

packages/commercial-development-agent/vitest.config.mjs:

   test: {
-    testTimeout: 45_000,
     coverage: {

packages/commercial-development-agent/src/workflow-contract.test.mjs:

linuxX64Test(
  'registers a NVIDIA model absent from the bundled OpenCode catalog without discovery',
  () => {
    // ...
  },
  45_000,
);
🤖 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-development-agent/vitest.config.mjs` at line 5, Remove
the global testTimeout setting from the Vitest configuration and apply the
45-second timeout only to the slow OpenCode binary test in
workflow-contract.test.mjs. Pass 45_000 as the third argument to that test’s
invocation, preserving the existing spawnSync timeout and all other tests’
default timeout.
.github/workflows/ci.yml (1)

50-51: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

validatecompose_runtime에 직렬 연결하면 모든 PR의 CI 시간이 늘어납니다.

두 작업은 서로 산출물을 공유하지 않습니다. needs: compose_runtime은 게이트 역할만 하며, 그 대가로 validate의 lint/typecheck/test/build가 Compose 기동이 끝날 때까지 시작하지 못합니다. 두 작업을 병렬로 실행하고 각각을 필수 체크로 등록하면 동일한 게이트 효과를 유지하면서 대기 시간을 줄일 수 있습니다.

계약 테스트가 needs: compose_runtime 문자열을 단언하므로, 변경 시 packages/commercial-development-agent/src/workflow-contract.test.mjs의 해당 단언도 함께 갱신하십시오.

🤖 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 @.github/workflows/ci.yml around lines 50 - 51, Remove the unnecessary needs:
compose_runtime dependency from the validate job in the workflow so validation
runs in parallel, while keeping both jobs as required checks. Update the
corresponding assertion in workflow-contract.test.mjs that currently expects the
needs dependency.
packages/commercial-development-agent/src/workflow-contract.test.mjs (1)

367-372: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

지역 변수 compose가 모듈 수준 compose를 가립니다. 이름을 바꾸십시오.

모듈 수준의 compose(24-26행)는 compose.yaml의 내용입니다. 이 지역 변수는 워크플로 단계 텍스트입니다. 같은 이름이 두 가지 의미를 가집니다. 바로 아래 테스트(403-412행)는 모듈 수준 compose를 사용하므로 현재 동작은 정확합니다. 그러나 이후에 단언을 블록 사이로 옮기면 의미가 조용히 바뀝니다. 지역 변수를 composeStep으로 바꾸십시오.

♻️ 제안 수정
-    const compose = step(
+    const composeStep = step(
       'Validate Compose configuration through trusted boundary',
     );
-    expect(compose).toContain('docker compose');
-    expect(compose).toContain('--file "$MODEL_WORKSPACE/compose.yaml"');
-    expect(compose).toContain('--project-directory "$MODEL_WORKSPACE"');
-    expect(compose).toContain('config --quiet');
-    expect(compose).not.toContain('sudo -u opencode_model');
-    expect(compose).not.toContain('${{ secrets.');
+    expect(composeStep).toContain('docker compose');
+    expect(composeStep).toContain('--file "$MODEL_WORKSPACE/compose.yaml"');
+    expect(composeStep).toContain('--project-directory "$MODEL_WORKSPACE"');
+    expect(composeStep).toContain('config --quiet');
+    expect(composeStep).not.toContain('sudo -u opencode_model');
+    expect(composeStep).not.toContain('${{ secrets.');
🤖 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-development-agent/src/workflow-contract.test.mjs` around
lines 367 - 372, Rename the local variable returned by step() in the workflow
contract test from compose to composeStep, and update its associated assertions.
Leave the module-level compose fixture and the later assertions that reference
it unchanged.
🤖 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 `@apps/identity-service/migrations/0004_session_authentication_age.sql`:
- Around line 31-34: Split the migration around the
sessions_authentication_not_after_creation constraint and authenticated_at
nullability: add the CHECK constraint as NOT VALID, validate it in a separate
migration step, then perform the final SET NOT NULL transition only after
validation succeeds. Preserve the existing constraint expression and
identity.sessions target while keeping the final locking operation as short as
possible.

In
`@apps/identity-service/src/session-authentication-migration.integration.test.ts`:
- Around line 46-148: Extend the “backfills every legacy rotated session...”
integration test coverage to create separate invalid chains where
rotated_from_id crosses users and crosses workspaces. Run the authentication
migration for each case and assert it fails, confirming authenticated_at
constraints are not bypassed and invalid lineage is rejected. Keep the existing
valid-chain assertions intact.

In `@packages/commercial-development-agent/src/workflow-contract.test.mjs`:
- Around line 216-222: Replace the inferred executable path construction in the
workflow contract test with a command or repository-defined path that resolves
the actually installed OpenCode executable, such as the project’s pnpm exec
mechanism. Remove the dependency on realpathSync(opencode-ai) and the hardcoded
opencode-linux-x64/bin/opencode path while preserving the test’s executable
invocation behavior.

In `@packages/commercial-readiness/src/buyer-gaps.mjs`:
- Around line 363-379: Update attachBuyerGapEvidence to validate that evidence
is a plain object and that evidence.unresolved, evidence.unknown, and
evidence.resolved are all arrays before accessing their length or mapping them;
throw the established clear validation error for invalid evidence instead of
allowing a TypeError.

In `@packages/commercial-readiness/src/render.mjs`:
- Around line 34-38: 함수 capabilityList, renderCanonicalBuyerGaps,
renderCapabilityEvidenceGaps, renderCommercialReadinessIssue에 기여자가 구현을 추적하지 않고
이해할 수 있는 JSDoc을 추가하십시오. 각 함수의 입력과 반환값, 새 buyer-gap 보고서 계약에서의 역할, canonical
buyer-gap과 capability evidence의 구분을 명시하고 기존 동작은 변경하지 마십시오.

---

Outside diff comments:
In `@apps/identity-service/src/auth-security.ts`:
- Around line 208-224: Update the SessionRecord and ActiveSession interface
declarations with explanatory docstrings documenting that authenticatedAt is the
UTC timestamp of the initial authentication and remains unchanged after session
rotation. Keep the contract clear for new contributors and preserve the existing
field definitions.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 50-51: Remove the unnecessary needs: compose_runtime dependency
from the validate job in the workflow so validation runs in parallel, while
keeping both jobs as required checks. Update the corresponding assertion in
workflow-contract.test.mjs that currently expects the needs dependency.

In `@packages/commercial-development-agent/src/workflow-contract.test.mjs`:
- Around line 367-372: Rename the local variable returned by step() in the
workflow contract test from compose to composeStep, and update its associated
assertions. Leave the module-level compose fixture and the later assertions that
reference it unchanged.

In `@packages/commercial-development-agent/vitest.config.mjs`:
- Line 5: Remove the global testTimeout setting from the Vitest configuration
and apply the 45-second timeout only to the slow OpenCode binary test in
workflow-contract.test.mjs. Pass 45_000 as the third argument to that test’s
invocation, preserving the existing spawnSync timeout and all other tests’
default timeout.

In `@packages/commercial-readiness/package.json`:
- Around line 7-10: package.json의 build, lint, typecheck 스크립트에서 반복된 파일 목록을 node
--check src/*.mjs 단일 글로브로 통일하고, build에도 schema.mjs, audit.mjs, pr-gate.mjs,
render.mjs 검사를 포함하십시오.

In `@packages/commercial-readiness/src/audit.mjs`:
- Around line 234-237: Update the workflow and design documentation for
renderCommercialReadinessIssue to explicitly state that stored
life-os.commercial-readiness-report.v1 reports remain compatible when
summary.unresolved_buyer_gaps is absent. Document that this legacy form is
treated as buyer-gap evidence “not evaluated,” while reports containing the new
buyer-gap field continue to be supported.

In `@packages/commercial-readiness/src/buyer-gap-audit.test.mjs`:
- Around line 8-31: Update the manifest fixture to pass through
validateCapabilityManifest, following the pattern used by the manifest() helper
in audit.test.mjs, so it includes the validated schema and matches the real
manifest contract before evaluateCapabilities consumes it.
- Around line 43-72: Extend the test around evaluate to verify the legacy result
shape when buyerGapEvidence is undefined: assert the complete returned structure
is byte-for-byte equivalent or that no new buyer-gap fields are present, while
retaining the existing maturity comparisons. Populate the configured evidence
with representative resolved and unknown entries, then assert buyer_gap_resolved
and unknown_buyer_gap_states report the expected nonzero counts alongside the
existing unresolved count.

In `@packages/commercial-readiness/src/buyer-gap-cli.mjs`:
- Around line 86-99: Move the GITHUB_TOKEN validation in the CLI entry function
ahead of the Promise.all JSON reads and subsequent manifest, registry, snapshot,
and policy validation. Preserve the existing required-token condition and error
behavior, but fail immediately before any file I/O.
- Around line 76-82: Update writeAtomic so a failed rename removes the temporary
file created by writeFile. Wrap the rename operation in failure handling, clean
up temporary on error, and preserve the original rename failure after cleanup.

In `@packages/commercial-readiness/src/buyer-gap-cli.test.mjs`:
- Around line 1-53: Extend buyer-gap CLI tests beyond parseBuyerGapArguments to
cover readJson and writeAtomic security and integrity failures: reject symlink
inputs, oversized inputs, and invalid JSON with the specified messages, verify
runBuyerGapAudit rejects an empty GITHUB_TOKEN, and assert writeAtomic output
files use mode 0o600. Also add parser rejection cases for option values
beginning with -- and values exceeding 500 characters, preserving the existing
invalid-command error behavior.

In `@packages/commercial-readiness/src/buyer-gaps.mjs`:
- Around line 342-350: In the issue-state handling around resolutionFor, remove
the redundant issue.state === 'closed' check and rely on the prior unknown/open
branches to reach this path. Keep the resolution !== null guard and existing
resolved evidence behavior unchanged.
🪄 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: a0b9e973-4d08-4140-8a81-901765499a0b

📥 Commits

Reviewing files that changed from the base of the PR and between 8768500 and 45a6b1d.

📒 Files selected for processing (33)
  • .github/workflows/ci.yml
  • .github/workflows/commercial-readiness.yml
  • .github/workflows/opencode-commercial-development.yml
  • ARCHITECTURE.md
  • CHANGELOG.md
  • apps/identity-service/migrations/0004_session_authentication_age.sql
  • apps/identity-service/src/auth-security.ts
  • apps/identity-service/src/authentication-age.test.ts
  • apps/identity-service/src/oauth-callback-application.ts
  • apps/identity-service/src/oauth-http-boundary.ts
  • apps/identity-service/src/postgres-security-repositories.integration.test.ts
  • apps/identity-service/src/postgres-security-repositories.test.ts
  • apps/identity-service/src/postgres-security-repositories.ts
  • apps/identity-service/src/session-authentication-migration.integration.test.ts
  • compose.yaml
  • docs/operations/opencode-commercial-development-loop.md
  • docs/research/2026-08-07-opencode-commercial-development-loop-standards.md
  • docs/superpowers/plans/2026-08-07-opencode-commercial-development-loop.md
  • docs/superpowers/specs/2026-08-07-opencode-commercial-development-loop-design.md
  • packages/commercial-development-agent/src/workflow-contract.test.mjs
  • packages/commercial-development-agent/vitest.config.mjs
  • 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 apps/identity-service/migrations/0004_session_authentication_age.sql Outdated
Comment thread packages/commercial-readiness/src/buyer-gaps.mjs
Comment thread packages/commercial-readiness/src/render.mjs

@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: 2

🤖 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
`@apps/identity-service/tests/session-authentication-migration.integration.test.ts`:
- Around line 8-9: Update TEMPORARY_DATABASE_NAME to include a unique
per-test-run value such as a UUID, ensuring parallel executions on the same
PostgreSQL cluster use isolated temporary databases while preserving the
existing database setup and cleanup flow.

In `@packages/commercial-readiness/src/buyer-gaps.mjs`:
- Around line 367-374: Extend the evidence validation around the unresolved,
resolved, and unknown arrays to validate every element against the existing
buyer-gap item schema, reusing validateBuyerGapSnapshot’s item validation or a
dedicated equivalent. Reject the entire function when any element has invalid
gap_id, issue_number, capability_ids, state, resolution, or unexpected fields,
and add a buyer-gap-validation.test.mjs failure case covering an invalid array
element.
🪄 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: 0ea9fdeb-1577-441c-a66b-3ce60b37cd03

📥 Commits

Reviewing files that changed from the base of the PR and between 45a6b1d and 56c396f.

📒 Files selected for processing (8)
  • apps/identity-service/migrations/0004_session_authentication_age.sql
  • apps/identity-service/migrations/0005_finalize_session_authentication_age.sql
  • apps/identity-service/tests/session-authentication-migration-contract.test.ts
  • apps/identity-service/tests/session-authentication-migration.integration.test.ts
  • packages/commercial-development-agent/src/workflow-contract.test.mjs
  • packages/commercial-readiness/src/buyer-gap-validation.test.mjs
  • packages/commercial-readiness/src/buyer-gaps.mjs
  • packages/commercial-readiness/src/render.mjs
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/identity-service/migrations/0004_session_authentication_age.sql
  • packages/commercial-readiness/src/buyer-gap-validation.test.mjs
  • packages/commercial-readiness/src/render.mjs

Comment thread apps/identity-service/tests/session-authentication-migration.integration.test.ts Outdated
Comment thread packages/commercial-readiness/src/buyer-gaps.mjs Outdated

Copy link
Copy Markdown
Contributor Author

@opencode-agent Fix only the current causal GREEN gap on exact contributor head 174b40903d094d9be686bbfe3c26b523cfff7515; refuse to write if the head moved.

Fresh CI proved the new regression RED in packages/commercial-readiness/src/buyer-gap-validation.test.mjs: rejects malformed items inside every buyer-gap evidence collection fails with Missing expected exception.. Implement the narrowest fix in packages/commercial-readiness/src/buyer-gaps.mjs only (plus test adjustment only if the stated contract itself is incorrect):

  • validate every item in evidence.unresolved, evidence.resolved, and evidence.unknown before attaching it;
  • require the exact keys gap_id, issue_number, capability_ids, state, resolution;
  • require bounded valid gap/capability IDs, positive safe issue numbers, a non-empty bounded unique capability list, and no unexpected fields;
  • require collection semantics: unresolved=state: open + null resolution; unknown=state: unknown + null resolution; resolved=state: closed + one of completed|not_planned|duplicate;
  • bound collections to the existing buyer-gap limits and reject duplicate gap/issue ownership across the three arrays;
  • preserve configured capability maturity and produce only normalized copied evidence in the report;
  • keep all errors credential-free as Buyer gap evidence is invalid.

Run the focused commercial-readiness test first and prove GREEN, then the package/full relevant validation. Do not modify identity-session feature files, workflow authority, merge/release policy, secrets, or documentation in this task.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 2

🤖 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
`@apps/identity-service/tests/session-authentication-migration.integration.test.ts`:
- Around line 80-89: 정리 로직에서 migrationPool.end()와 데이터베이스 삭제를 각각 중첩된 finally 블록으로
감싸, 앞선 단계가 실패해도 lockHeld인 경우 pg_advisory_unlock이 항상 실행되고 마지막으로 adminPool.end()도
호출되도록 수정하십시오.

In `@packages/commercial-readiness/src/buyer-gaps.mjs`:
- Around line 362-364: Document the contracts of failBuyerGapEvidence,
normalizeAttachedGapEvidence, and normalizeBuyerGapEvidence with JSDoc. Describe
each function’s inputs, normalization result, and failure conditions, including
the error behavior of failBuyerGapEvidence, so a new contributor can understand
the API without inspecting the implementation.
🪄 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: 39a4ab9d-960b-43e5-857c-278dcdf043eb

📥 Commits

Reviewing files that changed from the base of the PR and between 56c396f and e2ae810.

📒 Files selected for processing (4)
  • apps/identity-service/tests/session-authentication-migration.integration.test.ts
  • packages/commercial-development-agent/src/workflow-contract.test.mjs
  • packages/commercial-readiness/src/buyer-gap-validation.test.mjs
  • packages/commercial-readiness/src/buyer-gaps.mjs
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/commercial-readiness/src/buyer-gap-validation.test.mjs
  • packages/commercial-development-agent/src/workflow-contract.test.mjs

Comment thread apps/identity-service/tests/session-authentication-migration.integration.test.ts Outdated
Comment thread packages/commercial-readiness/src/buyer-gaps.mjs
@seonghobae
seonghobae merged commit ab6c15f into main Aug 9, 2026
32 of 34 checks passed
seonghobae added a commit that referenced this pull request Aug 9, 2026
…136)

Add a fail-closed recent-authentication policy that uses the preserved authentication provenance from #134, distinguishing authentication age from session rotation. Includes test-first boundary, stale/future/malformed provenance rejection, and exact-head CI/security validation.
seonghobae added a commit that referenced this pull request Aug 9, 2026
* test(planning): define durable Today synchronization contract

* feat(planning): implement durable Today domain contract

* test(planning): define atomic Today persistence contract

* feat(planning): persist Today with atomic optimistic writes

* feat(planning): add durable Today persistence schema

* feat(planning): compose durable Today runtime

* test(planning): define Today HTTP precondition boundary

* feat(planning): enforce authenticated Today HTTP preconditions

* feat(planning): expose authenticated Today sync API

* fix(planning): avoid duplicate migration ordinal

* fix(planning): order durable Today migration after repository constraints

* test(planning): verify durable Today PostgreSQL behavior

* test(web): define authenticated Today sync BFF contract

* feat(web): add authenticated durable Today BFF

* feat(web): expose same-origin Today sync route

* test(web): include durable Today sync in quality gates

* test(web): define explicit local-to-workspace Today migration

* feat(web): implement explicit local-to-workspace Today migration

* test(web): verify workspace Today migration path

* feat(web): label local and durable Today states

* feat(web): localize durable Today sync states

* feat(web): add explicit durable Today controls

* feat(web): connect explicit workspace Today controls

* test(web): verify explicit durable Today migration

* test(ci): require browser journey verification

* ci(web): execute Playwright buyer journeys

* test(web): verify stale Today reconciliation

* docs(research): ground durable Today synchronization

* docs(planning): add durable Today operations runbook

* docs(planning): design durable Today workspace sync

* docs(planning): plan durable Today workspace sync

* test(web): preserve local Today across retry

* test(web): scope accessibility live-region assertions

* test(web): bind durable search acceptance to semantic controls

* test(web): follow the current Today capture label

* style(planning): restore canonical provider formatting

* style(planning): format concurrent Today assertion

* fix(web): remove unreachable Today save disabled check

* fix(web): narrow optional Today request body

* test(web): make Today fetch fixtures exact-optional safe

* test(web): preserve explicit optional fetch init in Today sync

* style(web): format Today workspace synchronization

* fix(planning): acquire Today advisory locks in order

* test(planning): enforce deterministic Today lock order

* test(planning): stress identical Today replay concurrency

* style(web): format Today sync boundary

* style(web): format Today workspace client

* style(web): format Today sync tests

* style(web): format Today workspace tests

* test(planning): match SQL client result contract

* test(planning): isolate lock-order integration fixture

* test(planning): move lock-order fixture out of production source

* test(planning): remove dynamic SQL from lock-order fixture

* style(web): format Today workspace sync client

* style(web): format Today sync boundary

* style(web): format Today sync tests

* fix(web): parse strong ETags with valid regex syntax

* fix(web): use valid strong ETag parser in BFF

* feat(identity): enforce recent-authentication policy for data rights (#136)

Add a fail-closed recent-authentication policy that uses the preserved authentication provenance from #134, distinguishing authentication age from session rotation. Includes test-first boundary, stale/future/malformed provenance rejection, and exact-head CI/security validation.

* test(planning): exercise Today concurrency independently

* fix(planning): make Today date constraint DateStyle-independent

* fix(planning): type Today SQL parameters explicitly

* test(identity): bind data-rights ownership to recent authenticated session (#137)

* test(identity): define recent authentication gate for data rights

* feat(identity): enforce recent authentication policy

* test(identity): define authenticated data-rights context boundary

* feat(identity): bind data-rights export to recent authenticated session

* fix(planning): serialize Today writes in explicit transactions

* fix(planning): pin Today transactions to one PostgreSQL connection

* test(planning): cover transactional Today persistence

* test(planning): verify transaction lifecycle and cleanup

* test(ci): bind Today concurrency to contributor head

* test(planning): remove SQL-text lock-order surrogate

* ci: capture exact Today prettier patch

* test(web): expose in-flight Today save overwrite

* fix(web): preserve edits during Today save

* ci: apply bounded Today formatting

* ci: expose read-only Today format patch

* style(web): format Today sync client

* style(web): format Today workspace sync

* style(web): format Today sync tests

* chore(ci): remove Today format diagnostic

* test(planning): reject malformed Today lookup scope before SQL

* ci: verify Today lookup validation red

* ci: apply verified Today lookup validation

* fix(ci): compare repair lease to contributor head

* fix(ci): include staged self-removal in repair lease

* test(planning): reject malformed Today repository lookups

* test(planning): distinguish corrupted Today persistence

* fix(planning): classify invalid Today persistence separately

* fix(planning): validate Today lookup scope before SQL

* test(planning): fail explicitly on leaked Today connections

* chore(ci): remove superseded Today repair workflow

* fix(web): lint complete source globs

* test(planning): make Today concurrency cleanup deterministic

* docs(today): classify standards publication status

* docs(today): align validation and readiness plan

* test(planning): define shared Today invariants contract

* feat(planning): centralize Today validation invariants

* refactor(planning): reuse shared Today invariants

* refactor(planning): share Today invariants with persistence

* refactor(planning): reuse Today invariants at HTTP boundary

* test(planning): import Today persistence error from domain boundary

* test(planning): use shared Today persistence error boundary

* fix(web): preserve destructive-copy warning in Korean

* test(ci): bind browser acceptance to its workflow job

* test(web): close Today BFF authority branch gaps

* ci(web): stage one-shot canonical formatter

* style(web): apply canonical formatter output

* feat(identity): persist data-rights request receipts (#138)

* test(identity): define durable data-rights request ledger

* feat(identity): persist data-rights request state

* feat(identity): add data-rights request ledger schema

* test(identity): preserve data-rights receipts through erasure

* fix(identity): retain data-rights receipts after erasure

* chore(identity): sequence data-rights request migration

* chore(identity): remove duplicate migration sequence

* docs(identity): record durable data-rights ledger boundary

* test(identity): expose request-id collision as domain conflict

* fix(identity): normalize request ledger conflicts

* test(identity): harden data-rights ledger integration harness

* test(identity): cover dual request conflict evidence

* docs(identity): align data-rights ledger implementation status

* docs(changelog): record durable data-rights ledger

* fix(identity): resolve ledger migration path portably

* test(identity): require immutable terminal receipt storage

* test(identity): model pg timestamp rows as Date values

* test(identity): satisfy pg parameter mutability contract

* fix(identity): enforce immutable terminal data-rights receipts

* test(identity): assert immutable receipt no-op at database boundary

* test(identity): bind migration fixture lock to one PostgreSQL session

* test(web): expose Today media-type and conflict coupling

* test(web): execute Today review regressions

* fix(web): preserve existing test dependencies

* fix(web): normalize Today media types and conflict semantics

---------

Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

2 participants