Skip to content

fix(acquisition): bind commercial source records - #526

Draft
seonghobae wants to merge 46 commits into
mainfrom
fix/acquisition-source-document-authority
Draft

fix(acquisition): bind commercial source records#526
seonghobae wants to merge 46 commits into
mainfrom
fix/acquisition-source-document-authority

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Scope

Keep acquisition/commercial evidence fail closed: retained source-document byte identity, distributed-package metadata identity, filesystem race resistance, report schema, runner-assignment evidence integrity and buyer/legal authority separation. Hashes, CI, SBOMs, scanners and source licensing do not create acquisition/legal truth.

Current exact authority — 2026-09-06 KST

Protected Noema is main@e26d771470a4ece873c367b40b3cd6cb03ac7de3. Ordinary two-parent/non-force restack bd4c9079b5c81c0cd63cae6fb0bdd4a845e9fbf2 preserves bounded retained {path, sha256} source authority, protected package digest semantics and the filesystem regressions while adopting merged #527 OIDC trust. The global product-gap baseline remains owned only by #547.

Fresh exact-head reviewer-ci 34026914275, ci 34026914283, patch-validator-image 34026914307, and required Security Scan 34026914268 are queued. Predecessor GREEN does not transfer. Keep Draft until current terminal gate/review evidence is complete.

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Revenue 및 transfer evidence의 source_documents가 문자열 label에서 보존 artifact의 {path, sha256} binding으로 변경되었다. 감사 로직은 artifact 존재, digest 일치, 최대 32개 제한을 검증한다. 파일 읽기와 실패한 출력 정리는 fail-closed 방식으로 강화되었다.

Changes

Acquisition evidence integrity

Layer / File(s) Summary
다이제스트 검증 계약 및 감사 로직
docs/acquisition-readiness-2b.md, docs/evidence-templates/*.json, scripts/acquisition-readiness-audit.mjs
source_documents가 canonical repository-relative path와 64-hex sha256을 포함하는 1~32개의 retained artifact binding이 되었다. 감사 로직은 각 binding을 검증하고 최대 32개로 제한한다.
안정적 artifact 읽기 및 출력 정리
scripts/lib/acquisition-data-room-integrity.mjs, scripts/lib/acquisition-private-output.mjs
readStableFile이 hardlink, close 실패, close 이후 경로 교체를 거부한다. 실패한 출력은 no-follow descriptor와 inode 확인 후 truncate한다.
무결성 회귀 테스트와 픽스처 갱신
test/acquisition-*.test.ts
테스트가 실제 원천 문서를 생성하고 SHA-256 binding을 사용한다. 불일치 digest, 32개 초과 입력, hardlink artifact, close 실패, 경로 교체와 동시 출력 교체를 검증한다.
준비도 문서 및 추적 정보 정렬
docs/buyer-due-diligence-index.md, docs/product-technical-gap-baseline.md, docs/TRACEABILITY.md, CHANGELOG.md
Evidence 요구사항, 예시 날짜, 성숙도 설명과 변경 이력을 새 binding 계약에 맞게 갱신했다. digest 일치만으로 기록의 진실성이나 승인 권한을 증명하지 않는다는 제한을 명시했다.

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

Merge Risk: 🟡 Moderate · up to a0d28

Failed-output cleanup can hang indefinitely if a locally authorized actor replaces the target with a FIFO, delaying report generation and potentially retaining the writer lock. Merge should wait for the non-blocking cleanup fix or explicit owner acceptance.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 12 files. (1 skipped: … 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 제목은 revenue 및 transfer의 commercial source records를 구조화된 바인딩으로 변경하는 PR의 주요 목적을 명확하고 간결하게 설명합니다.
Full details: Docstring Coverage

Explanation

Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 12 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ 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/acquisition-source-document-authority

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.

@devin-ai-integration devin-ai-integration 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

…with README/license/runner-pin PR #530/#532

Fast-forwards this PR's branch past two merges it was behind (5aad3e4 ->
6b2b3e9): PR #532 (pinned GitHub-hosted runner selectors, fixing a
floating-runner CI stall) and PR #530 (product-first README, Apache-2.0
root LICENSE + docs/index.md, dependency-license gap tracking for
issue #531). Both merged cleanly via git's 3-way merge except for one
genuine text conflict, plus one non-textual semantic incompatibility
surfaced only by running the test suite.

Text conflict resolved:
- docs/product-technical-gap-baseline.md: both PRs independently
  rewrote the same "Authority and update rule" snapshot line and the
  same three-row "Live external observation" table (this PR's lane
  recorded open-PR/hourly-loop governance state as of dd6ff2a; #530's
  lane recorded README/license-lane state as of 5aad3e4). Combined
  both lanes' distinct observations into one merged snapshot paragraph
  and a six-row table (kept both PR lanes' rows, merged the two
  differently-worded "Release/publication" rows into one), and
  corrected the now-stale "PR #530 is open" claim to reflect that it
  has since merged into protected main at 6b2b3e9 (directly evidenced
  by this merge, not fabricated). No test asserts this table's exact
  wording (test/documentation-architecture-contract.test.ts only checks
  for specific issue-number substrings), so no other file needed this
  treatment.

Files independently changed by only one side (ci.yml,
patch-validator-image.yml, reviewer-ci.yml, README.md,
docs/LICENSING_AND_IP_TRANSFER.md,
docs/security/private-vulnerability-reporting-audit.md,
scripts/lib/private-vulnerability-reporting-audit.mjs,
scripts/private-vulnerability-reporting-audit.mjs, associated tests)
and files both sides touched in disjoint regions (CHANGELOG.md;
scripts/acquisition-readiness-audit.mjs, where this PR's new
MAX_SOURCE_DOCUMENTS/source_documents digest-binding logic near the
top of the file and #530's packageDistributionApplies/
packageMetadataDeclared licensing logic near the bottom do not
overlap) merged automatically with no marker conflicts; verified by
diffing each side against the merge base and confirming both sides'
additions are present in the merged result.

Semantic fix (not a git conflict, found via tests):
- test/acquisition-source-only-license.test.ts (added by #530, so
  written without knowledge of this PR's stricter contract) built its
  transfer-evidence.json fixture with the pre-#526 loose format
  `source_documents: ["legal/review-record.pdf"]` (a bare string
  label). This PR's whole purpose is to replace that with bounded
  structured `{path, sha256}` retained-artifact bindings, so the new
  validateDigestBoundArtifact check correctly rejected the old-format
  fixture ("source_documents[0] artifact binding required"). Updated
  the fixture to emit a real digest-bound artifact via the file's own
  existing digestArtifact() helper, matching the same pattern already
  used by the currently-passing test/acquisition-transfer-rights.test.ts
  fixture. This is the deliberate, intended tightening this PR
  introduces; the fixture simply hadn't been written against it yet.

Verification:
- npm run typecheck: clean, no errors.
- npm test (Node 24.19.0, matching devEngines/CI): 3737 passed, 0
  failed, 1 pre-existing unrelated skip, after the fixture fix above.
- Established a pre-existing-failure baseline by running the full
  suite twice on an unmodified fresh origin/main checkout: it is not
  perfectly stable under this sandbox's full-parallel test run (534/534
  on one run, 1 failure in test/acquisition-review-regressions.test.ts
  on another, both with byte-identical source) - a pre-existing,
  environment-specific flake where tests that fail-closed-verify the
  live working tree against exact HEAD, or share one on-disk output
  directory, race against sibling test files under full-suite
  parallelism. The 5 non-source_documents failures initially observed
  on this branch before finishing the merge commit (acquisition-data-
  room-manifest x2, acquisition-output-symlink x2,
  dependency-license-inventory-release-wiring x1) were an artifact of
  running those exact-HEAD integrity checks against a working tree that
  legitimately did not yet match any commit (HEAD was still eaebeec
  while the tree already held the in-progress merge); each passes
  individually once run against a worktree whose HEAD matches its tree,
  confirmed by rerunning them standalone both before and after this
  commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4

Copy link
Copy Markdown
Contributor Author

Merged current main in — conflicts resolved, tests verified

This PR's base was two merges behind main (5aad3e416b2b3e90: PR #532's pinned GitHub-hosted runner selectors, then PR #530's product-first README + Apache-2.0 root LICENSE + docs/index.md + dependency-license gap tracking for #531). Merged origin/main into the branch, resolved the conflict, fixed one non-textual semantic incompatibility surfaced only by the test suite, verified, and pushed. Head is now 545c247 on top of base 6b2b3e9.

Text conflict (1 file)

docs/product-technical-gap-baseline.md — both PRs independently rewrote the same "Authority and update rule" snapshot line and the same three-row "Live external observation" table: this PR's lane recorded open-PR/hourly-loop governance state as of dd6ff2aa; #530's lane recorded README/license-lane state as of 5aad3e41. Resolution: combined both lanes' distinct observations into one merged snapshot paragraph and a six-row table (kept every row from both lanes, merged the two differently-worded "Release/publication" rows into one), and corrected the now-stale "PR #530 is open" claim to reflect that it has since merged into protected main at 6b2b3e90 — a fact directly evidenced by doing this merge, not fabricated. No test asserts this table's exact wording (test/documentation-architecture-contract.test.ts only checks for specific issue-number substrings), so no other file needed this treatment.

Every other touched file (.github/workflows/{ci,patch-validator-image,reviewer-ci}.yml, README.md, docs/LICENSING_AND_IP_TRANSFER.md, docs/security/private-vulnerability-reporting-audit.md, the private-vulnerability-reporting scripts/tests, CHANGELOG.md, and scripts/acquisition-readiness-audit.mjs) merged automatically with no marker conflicts. I diffed each side against the merge base individually to confirm this wasn't a false negative — most were changed by only one side; CHANGELOG.md and scripts/acquisition-readiness-audit.mjs were touched by both sides but in disjoint regions (this PR's MAX_SOURCE_DOCUMENTS/digest-binding logic near the top vs. #530's packageDistributionApplies/packageMetadataDeclared licensing logic near the bottom), and I confirmed both sides' additions are present in the merged result.

Semantic fix found via tests (not a git conflict)

test/acquisition-source-only-license.test.ts (added by #530, written without knowledge of this PR's contract) built its transfer-evidence.json fixture with the pre-#526 loose format source_documents: ["legal/review-record.pdf"] — a bare string label. This PR's whole purpose is replacing that with bounded structured {path, sha256} retained-artifact bindings, so the new validateDigestBoundArtifact check correctly rejected the old-format fixture ("source_documents[0] artifact binding required"). Updated the fixture to emit a real digest-bound artifact via the file's own existing digestArtifact() helper, matching the pattern already used by the currently-passing test/acquisition-transfer-rights.test.ts fixture. This is this PR's own deliberate, intended tightening — the fixture just hadn't been written against it yet.

Verification (Node 24.19.0, matching devEngines/CI)

  • npm run typecheck: clean.
  • npm test: 3744/3744 tests passed (536/536 files), 0 failures, after the fixture fix above.
  • Established a pre-existing-failure baseline first by running the full suite twice on an unmodified fresh origin/main checkout: not perfectly stable under this sandbox's full-parallel run (534/534 clean on one run, 1 failure in test/acquisition-review-regressions.test.ts on another, byte-identical source both times) — a pre-existing, environment-specific flake where tests that fail-closed-verify the live working tree against exact HEAD, or share one on-disk output directory, can race against sibling test files under full-suite parallelism. Five failures I initially saw on this branch before finishing the merge commit (acquisition-data-room-manifest ×2, acquisition-output-symlink ×2, dependency-license-inventory-release-wiring ×1) were an artifact of running those exact-HEAD integrity checks against a working tree that legitimately didn't yet match any commit (HEAD was still the pre-merge eaebeec1 while the tree already held the in-progress merge); each passes standalone once HEAD matches the tree, confirmed before and after the merge commit.
  • Coverage: this branch reports 99.96%/99.97% (2 uncovered lines: scripts/lib/acquisition-data-room-integrity.mjs:276, scripts/lib/acquisition-private-output.mjs:119) against the repo's 100% gate. I confirmed this exact gap, with identical line numbers and percentages, already exists on this PR's own pre-merge head in isolation — both files are untouched by main, so this is a pre-existing condition of this PR's own new code, not something the merge introduced. Flagging it since the coverage gate will still fail CI as-is; not fixed here as it's outside the scope of a merge-conflict resolution.

Per standing governance, I did not merge, approve, or touch any merge/approve tool — that remains OpenCode/the merge scheduler's job once checks re-run on this head.


Generated by Claude Code


Generated by Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

@seonghobae seonghobae added bug Something isn't working priority: high High-priority or P1 work status: needs-review type: bug Defect or incorrect behavior labels Sep 2, 2026 — with ChatGPT Codex Connector

@devin-ai-integration devin-ai-integration 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.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 0 new potential issues.

Devin Review

Copy link
Copy Markdown
Contributor Author

Investigated independently — both previously-flagged problems are already fixed on the current head

Working in an isolated worktree off origin/fix/acquisition-source-document-authority, I re-checked the PR fresh before starting: the head had already moved past the 545c247 this task was queued against, to 85f14cd (no concurrent commits landed during my session, so nothing to merge). Investigating both problems from the prior triage against that head:

1. acquisition-private-output-atomic-replace.test.ts "staged inode" failure — already fixed

Root cause: this was a test-expectation bug, not a production bug. The original test (fails closed if the staged inode changes after atomic rename) asserted the output file was fully removed (lstatSync(...) .toBeUndefined()) after the fail-closed path detected a post-rename identity change. But scripts/lib/acquisition-private-output.mjs's actual, intentional design — stated in this PR's own body ("failed private-output cleanup is descriptor-bound, nonblocking, and capability-checked") and mirrored in the sibling acquisition-private-output-staging-parent-race.test.ts ("A path-level lstat followed by path-level unlink is not deletion authority... fail closed by retaining the failed staging inode for operator cleanup instead of deleting by pathname") — never deletes by pathname once descriptor identity can no longer be trusted. It neutralizes the file's content (truncates to empty) through the already-open, capability-checked descriptor and retains the inode for operator inspection. So the test's expectation contradicted the code's documented guarantee.

This was already corrected by the PR author in commits 8b67a93 ("align atomic-failure cleanup with descriptor-bound neutralization") and eaebeec ("keep descriptor-bound failure evidence non-authoritative"): the test is renamed to neutralizes the writer-owned replacement if its version changes after atomic rename and now asserts lstatSync(...).toBeDefined() and readFileSync(...) === "" — matching the real contract.

2. Coverage shortfall (99.96%/99.97%) — already closed

Closed by commit fbed32c ("cover neutralize-cleanup and afterClosePath fail-closed branches"), which added two real behavioral tests:

  • test/acquisition-data-room-integrity-branches.test.ts: covers the post-close lstatSync path-lookup-failure branch in readStableFile (the acquisition-data-room-integrity.mjs:276 gap) — asserts it returns null and still closes the descriptor.
  • test/acquisition-private-output.test.ts: covers the branch where O_NONBLOCK disappears from the filesystem capability object mid-cleanup (the acquisition-private-output.mjs:119 gap) — asserts the original error still propagates, the descriptor still closes, and the neutralization re-open is correctly skipped (openSync called exactly once).

Independent verification (Node v24.19.0 / npm 11.17.0, matching CI's pin)

  • npm run typecheck: clean.
  • npm test (vitest run --coverage), run twice end-to-end: 536/536 test files, 3745/3745 tests passed both times.
  • Coverage: 100% statements (6186/6186), 100% branches (4980/4980), 100% functions (720/720), 100% lines (5846/5846) — zero uncovered statements or branches (confirmed via the same coverage-final.json diagnostic scan ci.yml's verify job uses on failure).
  • One unrelated, non-deterministic failure surfaced in a single full-suite run: acquisition-review-regressions.test.ts > shares one configured data-room directory and restricts existing outputs to owner-only mode. This reproduces the exact pre-existing environment-specific flake already documented in this PR's own merge comment (tests sharing one on-disk data-room output directory racing under full-suite parallelism) — passes standalone every time, unrelated to either problem above, and out of this task's scope.

No code changes were needed from me — both issues were already resolved on 85f14cd before I started, so nothing was pushed. verify and the other required checks were still queued on GitHub at the time of this comment for the current head; the numbers above are from a from-scratch, isolated reproduction of the same command the verify job runs.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Triage: this branch was 63 commits behind main. Merged main in (no conflicts) and pushed. Verified locally on Node 24.19.0 after the merge:

  • npm run typecheck — clean
  • npm test — 3771/3773 passing; the 2 failures (acquisition-review-regressions.test.ts "shares one configured data-room directory..." and kpi-tail-streaming-integrity.test.ts "waits through TERM grace...") reproduce identically on a clean, unmodified main checkout in this sandbox, so they're pre-existing environment-specific (process-signal/file-permission) behavior in this container, not caused by this PR or the merge.

One review thread remains unresolved (PRRT_kwDOTJkwWc6eU3hR, an informational note on readStableFile close-uncertainty semantics) — it's a descriptive comment, not an actionable ask, so I left it for the author.


Generated by Claude Code

writeAcquisitionPrivateFile's pre-replacement verification open of an
existing target used O_RDONLY | O_NOFOLLOW without O_NONBLOCK, even
though O_NONBLOCK was already required as a filesystem capability. A
locally authorized actor racing the prior lstatSync regular-file check
with a FIFO substitution could make this open block indefinitely
waiting for a writer, wedging the writer lease and delaying report
generation (CodeRabbit finding on PR #526).

Add O_NONBLOCK to that open: it is a no-op on regular files, and on a
FIFO the open now returns immediately so the existing descriptor-type
check fails closed instead of hanging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KPmJErfkcHer4UVEgrQxUX

@devin-ai-integration devin-ai-integration 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.

Devin Review found 0 new potential issues.

Devin Review

@seonghobae
seonghobae marked this pull request as draft September 3, 2026 00:16
@seonghobae
seonghobae marked this pull request as ready for review September 3, 2026 15:57

Copy link
Copy Markdown
Contributor Author

Exact head 630612cd now has terminal evidence — all 15 checks completed success (verify, reviewer, verify-patch-validator-image, CodeQL ×3, Trivy, Scorecard ×2, osv-scan/osv-scanner, dependency-review), mergeable_state: clean, and every inline review thread resolved (Devin's last pass on this exact head: 0 new issues). Marked ready for review per this PR's own stated gate.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Fresh protected-base repair supersedes the prior exact-head block. Protected main is e1ac9d50f6c646f04be8c137c8acdc7200182fcd. Ordinary two-parent non-force merge ce40546e5131e4c1ebd01f427518c399c1268c94 preserves the commercial/acquisition retained-source evidence delta and inherits only the protected stateless GitHub installation-token regression. Current exact-head workflows ci 33872035062, reviewer-ci 33872035061, required Security Scan 33872035081, and patch-validator-image 33872035097 are queued/non-passing. The predecessor four-workflow success set, including the confirmed pre-#546 reviewer false-green, does not transfer. Keep Draft.

Copy link
Copy Markdown
Contributor Author

2026-09-06 KST post-#546 repair finding: protected base advanced to main@85b17014b8d46eacc95e096ca114568c321d0263. This acquisition lane overlaps protected/current authority in CHANGELOG.md and docs/product-technical-gap-baseline.md; its acquisition/data-room tests and evidence templates remain valid branch-owned delta. Do not resolve the stale base by wholesale replacement or Close. The safe path is an ordinary semantic restack that preserves acquisition evidence while reconciling the baseline to merged #546 truth, followed by fresh exact-head gates. No predecessor semantic GREEN transfers.

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

Labels

bug Something isn't working priority: high High-priority or P1 work status: needs-review type: bug Defect or incorrect behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants