Skip to content

Simplify Relay test accounting - #1202

Merged
sungjunlee merged 2 commits into
mainfrom
codex/1196-simplify-test-accounting
Aug 9, 2026
Merged

Simplify Relay test accounting#1202
sungjunlee merged 2 commits into
mainfrom
codex/1196-simplify-test-accounting

Conversation

@sungjunlee

@sungjunlee sungjunlee commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • remove generated test ledgers and the runtime inventory contract/checker
  • retain three direct fail-closed guards: CI coverage, test directives, and packaged script reachability
  • rename current vNext test artifacts to Relay terminology and narrow recover testing exports from 17 to 5

Measurements

  • accounting machinery: 4,345 -> 492 LOC (-3,853)
  • dispatch runtime: 6,238 -> 6,226 LOC (-12)
  • Relay tests: 14,009 -> 13,985 LOC (-24)

Verification

  • full serialized gate: 583 pass, 0 fail, 2 expected live-canary skips
  • independent adversarial review: LGTM, no remaining P1/P2
  • git diff --check

Fixes #1196
Parent #1197

Summary by CodeRabbit

  • 문서

    • 런타임 규모와 구성 안내가 고정된 생성 수치 대신 현재 설치된 파일 시스템을 기준으로 설명되도록 업데이트되었습니다.
    • 사용하지 않는 런타임 인벤토리와 테스트 원장 관련 문서 및 산출물이 정리되었습니다.
    • Relay Runtime 계약과 운영 가이드가 최신 검증 절차를 반영합니다.
  • 테스트 및 안정성

    • Relay 실행·복구·병합 흐름에 대한 검증이 강화되었습니다.
    • 안전하지 않은 파일, 잘못된 테스트 지시문, CI 구성 불일치를 자동으로 차단합니다.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 50 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: c7d23e76-60b9-4c06-944d-aa5f9bd20be5

📥 Commits

Reviewing files that changed from the base of the PR and between 24121e5 and 85cba6f.

📒 Files selected for processing (3)
  • backlog/sprints/2026-08-relay-runtime-slimming.md
  • docs/test-accounting-retirement-1196.md
  • tests/skills-lint/scripts/test-directives.test.js

Walkthrough

생성된 런타임·테스트 원장과 인벤토리를 제거했습니다. Relay 계약 검증을 현재 경로로 전환했습니다. CI 매트릭스와 테스트 지시문 검사를 추가하고, 테스트 픽스처와 명칭을 Relay 기준으로 정리했습니다.

Changes

Relay accounting and validation

Layer / File(s) Summary
회계 폐기와 문서 기준 갱신
CLAUDE.md, README.md, docs/..., references/architecture.md, backlog/sprints/...
생성된 ledger와 runtime inventory 참조를 제거했습니다. 설치된 파일시스템과 직접 검사를 기준으로 문서를 갱신했습니다.
런타임 fixture와 상태 검증
tests/relay-dispatch/fixtures/*, tests/relay-dispatch/scripts/facts.test.js, tests/relay-dispatch/scripts/run-fold.test.js, tests/relay-dispatch/scripts/recover-safe-staging.test.js
실행 fixture를 추가했습니다. journal 내구성, containment, recovery folding, stale proof, terminal 상태와 fail-closed 동작을 검증합니다.
Relay runtime 계약 전환
docs/contracts/relay-runtime-contracts.v1.json, tests/relay-dispatch/scripts/runtime-contract*.test.js, tests/relay-dispatch/scripts/dispatch.test.js
계약 gate와 테스트 경로를 Relay 기준으로 변경했습니다. 최상위 직접 테스트 등록을 식별하는 black-box 검증을 추가했습니다.
CI와 테스트 지시문 검사
tests/skills-lint/scripts/ci-relay-matrix.test.js, tests/skills-lint/scripts/test-directives.test.js
CI suite와 runner 구성을 파일시스템과 비교합니다. .only, todo, 허용되지 않은 skip, 계산된 등록을 fail-closed로 거부합니다.
테스트 표면 명칭 정리
tests/relay-*/**, tests/skills-lint/scripts/*
fixture 경로, 임시 디렉터리 접두사, 테스트 제목, fake GitHub 응답 이름에서 vNext 잔여 명칭을 제거했습니다.

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

Possibly related issues

  • #1197 — 생성 ledger와 inventory를 제거하고 Relay/vNext 전환 잔여물을 정리하는 상위 작업과 직접 연결됩니다.

Possibly related PRs

Poem

당근을 문 Relay 토끼, 원장을 지웠네
파일시스템 기준으로 깡충 뛰었네
계약은 닫히고, 검사는 또렷
CI 울타리도 빈틈없이
“vNext” 꼬리도 사라졌네.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive 대부분의 요구사항을 충족하지만 생성 파일 정리와 stale inventory 검사는 제외된 파일 때문에 확인할 수 없습니다. tests/ledger/vnext-baseline.generated.json 및 vnext-test-sites.generated.json을 제외 규칙 없이 검토하여 생성 산출물과 CI 검사를 확인하십시오.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 Relay 테스트 회계 단순화라는 주요 변경을 정확하고 간결하게 설명합니다.
Out of Scope Changes check ✅ Passed 문서 갱신, 테스트 픽스처와 가드 추가, vNext 명칭 변경은 모두 연결 이슈의 범위와 일치합니다.
✨ 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 codex/1196-simplify-test-accounting

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 24121e5943

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +193 to +195
const alternateSkipOption = /(?:\{|,)\s*skip\s*(?=[,}])/;
if (alternateSkipOption.test(optionObject.source)) violations.push(`${relative}: shorthand skip is forbidden`);
for (const match of optionObject.source.matchAll(/(?:\{|,)\s*skip\s*:/g)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject spread properties in test options

When a test supplies directives through an object spread, such as const hidden = { skip: true }; test("case", { ...hidden }, fn), these regexes find no explicit skip, only, or todo property, so the directive guard passes even though Node marks the test skipped. This bypasses the exact skip allowlist and permits ordinary Relay tests to disappear from CI; reject top-level option spreads or resolve them before validating directives.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 85cba6f. Top-level inline option spreads now fail closed; variable and literal spread regressions are covered while nested and callback-body spreads remain allowed. Focused skills-lint passed and an independent adversarial review found no remaining P1/P2.

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

🧹 Nitpick comments (8)
tests/skills-lint/scripts/test-directives.test.js (2)

216-275: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

itdescribe의 옵션 객체 skip 케이스를 주입 목록에 추가하세요.

Line 229부터의 주입 목록은 .skip 멤버 형태에서만 itdescribe를 다룹니다. 옵션 객체 형태(it('x', { skip: true }, ...))는 없습니다. 이 형태가 literalNameBeforetest( 전용 검색과 만나는 경로가 검증되지 않습니다. 해당 케이스를 추가하세요.

♻️ 제안 변경
     "test('hidden', { [`skip`]: true }, () => {});",
+    `it('hidden', { ${skipProperty} }, () => {});`,
+    `describe('hidden', { ${skipProperty} }, () => {});`,
🤖 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 `@tests/skills-lint/scripts/test-directives.test.js` around lines 216 - 275,
Extend the injections array in the “directive guard rejects injected
unauthorized directives” test with options-object skip cases for both it and
describe, using the same skipProperty pattern as the existing test case. Keep
the cases unauthorized and ensure they exercise the literalNameBefore handling
for non-test callers.

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

수동 스캐너에 짧은 설명 주석을 추가하세요.

regexStartsAt, withoutComments, stringMask는 문자열, 템플릿 리터럴, 정규식 리터럴, 문자 클래스를 직접 추적합니다. 각 함수의 목적과 처리 대상을 한두 줄로 명시하세요. 복잡한 로직에는 의미 있는 주석을 사용해야 합니다.

특히 Line 51의 "([{:;,=!&|?+-*%~"는 문자 클래스가 아니라 일반 문자열입니다. 이 점을 주석으로 밝히면 오해를 막을 수 있습니다.

Also applies to: 54-87, 89-117

🤖 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 `@tests/skills-lint/scripts/test-directives.test.js` around lines 49 - 52, Add
concise one- or two-line comments above regexStartsAt, withoutComments, and
stringMask describing each scanner’s purpose and the strings, template literals,
regex literals, and character classes it tracks. In particular, document that
the character sequence "([{:;,=!&|?+-*%"~" in regexStartsAt is an ordinary
string used for delimiter membership checks, not a regular-expression character
class.

Source: Coding guidelines

tests/skills-lint/scripts/ci-relay-matrix.test.js (2)

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

runnerStep의 주석 제거 동작을 문서화하세요.

runnerSteprun 블록 안의 주석 줄을 본문에서 제외합니다. 이 동작 때문에 주석이 삽입되어도 EXPECTED_RUNNER_STEP 비교는 통과합니다. 반대로 실행 줄을 주석으로 바꾸면 해당 줄이 사라져서 비교가 실패합니다. 이 의도를 짧은 주석으로 명시하세요. 복잡한 로직에는 의미 있는 주석을 사용해야 합니다.

또한 Line 115의 Math.min은 불필요합니다. String.prototype.slice는 인덱스를 길이로 자동 제한합니다.

♻️ 제안 변경
   const stepIndent = indent(steps[0].line);
   const body = [];
+  // Comment lines are dropped from the body. Commenting out an executable
+  // line therefore removes it and fails the exact-match comparison.
   for (let index = steps[0].index + 1; index < lines.length; index += 1) {
     if (lines[index].trim() && indent(lines[index]) <= stepIndent) break;
     if (lines[index].trim() && !lines[index].trimStart().startsWith("#")) {
-      body.push(lines[index].slice(Math.min(lines[index].length, stepIndent)));
+      body.push(lines[index].slice(stepIndent));
     }
   }
🤖 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 `@tests/skills-lint/scripts/ci-relay-matrix.test.js` around lines 105 - 124,
Update runnerStep to document that comment-only lines inside the run block are
excluded from the extracted body, while commenting out an executable line causes
the exact EXPECTED_RUNNER_STEP comparison to fail; retain the existing behavior.
Also simplify the body-line slicing by removing the unnecessary Math.min wrapper
and pass stepIndent directly to slice.

Source: Coding guidelines


198-217: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

약화 케이스의 오류 메시지 검증을 좁히세요.

Line 216의 정규식 /runner|step|exact|array|zero|serialize|guarded/i는 이 파일의 거의 모든 단언 메시지와 일치합니다. 예를 들어 잡 봉투 실패 메시지와 러너 실패 메시지가 서로 구분되지 않습니다. 그래서 케이스가 의도와 다른 이유로 실패해도 테스트는 통과합니다. 각 약화 케이스에 기대 메시지를 함께 지정하세요.

♻️ 제안 변경
-  for (const weakened of [
-    value.workflow.replace("files=()", ""),
-    ...
-  ]) assert.throws(() => assertCiContract({ testsDir: value.root, workflow: weakened }), /runner|step|exact|array|zero|serialize|guarded/i);
+  for (const [weakened, expected] of [
+    [value.workflow.replace("files=()", ""), /fail-closed shell program/],
+    [value.workflow.replace("    strategy:", "    if: always()\n    strategy:"), /job-level policy envelope/],
+    // ... 각 케이스에 해당 메시지를 지정
+  ]) assert.throws(() => assertCiContract({ testsDir: value.root, workflow: weakened }), expected);
🤖 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 `@tests/skills-lint/scripts/ci-relay-matrix.test.js` around lines 198 - 217,
Update the weakened-workflow cases in the assert.throws test around
assertCiContract to associate each mutation with its specific expected
error-message pattern. Replace the broad shared
/runner|step|exact|array|zero|serialize|guarded/i matcher with per-case
assertions that distinguish failures such as runner, step, exact, array, zero,
serialize, and guarded contract violations.
tests/relay-dispatch/fixtures/fake-codex.js (1)

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

fixture 제어 프로토콜을 주석으로 설명하세요.

-C, -o, stdin JSON, delay_ms, empty가 각각 작업 디렉터리, 결과 경로, 지연, 결과 파일 생성을 제어합니다. 이 의미가 호출부만으로는 명확하지 않습니다. 특히 empty가 작업 디렉터리 변경은 남기고 결과 artifact만 생략한다는 의도를 기록하세요.

주석 추가 예시
 const args = process.argv.slice(2);
+// The harness passes the working directory and result path as argv values.
 const value = (flag) => {
...
 const output = value("-o");
+// stdin controls delay injection and omission of the result artifact.
 let controls = {};
...
 if (controls.empty === true) process.exit(0);

As per coding guidelines, 복잡한 로직에는 의미 있는 주석을 추가해야 합니다.

🤖 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 `@tests/relay-dispatch/fixtures/fake-codex.js` around lines 7 - 21, fake-codex
fixture의 제어 프로토콜을 코드에 의미 있는 주석으로 설명하세요. `-C`와 `-o`가 작업 디렉터리와 결과 경로를 지정하고, stdin
JSON의 `delay_ms`가 지연을 제어하며 `empty`가 작업 디렉터리 변경은 유지한 채 결과 artifact 생성만 생략한다는 내용을
해당 파싱 및 분기 로직 주변에 명시하세요.

Source: Coding guidelines

tests/relay-dispatch/fixtures/write-containment-executor.js (1)

60-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

proof_in_result가 참이고 -o가 없으면 증거가 사라집니다.

63-64행은 controls.proof_in_result === true일 때 output이 있을 때만 기록합니다. output이 null이면 proof가 어디에도 저장되지 않습니다. 테스트는 증거 없이 실패하고 원인을 알기 어렵습니다. 이 조합에서는 worktree 내 containment-proof.json으로 되돌리거나 명시적으로 종료 코드를 설정하십시오.

♻️ 제안 수정
   if (controls.proof_in_result === true) {
-    if (output) fs.writeFileSync(output, bytes);
+    if (output) fs.writeFileSync(output, bytes);
+    else fs.writeFileSync(path.join(worktree, "containment-proof.json"), bytes);
   } else {
🤖 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 `@tests/relay-dispatch/fixtures/write-containment-executor.js` around lines 60
- 68, Update finish() so that when controls.proof_in_result === true and output
is absent, the proof is still persisted to worktree/containment-proof.json;
retain writing proof to output when output is provided and preserve the existing
executor status message for the non-proof-in-result path.
tests/relay-dispatch/scripts/facts.test.js (1)

481-514: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

주입된 fs 실패가 의도한 경계보다 먼저 발생합니다. appendFactrepairTornTail은 모두 본 작업 전에 readFacts({ eventsPath, fsModule })를 호출합니다. 두 테스트의 스텁은 해당 fs 메서드의 모든 호출에서 예외를 던집니다. 그러므로 예외는 항상 사전 판독 단계에서 발생하고, 테스트 이름이 지목하는 append open 경계와 repair journal-read 경계는 실행되지 않습니다. 두 테스트는 통과하지만 대상 코드를 검사하지 않습니다.

  • tests/relay-dispatch/scripts/facts.test.js#L481-L514: openSync 호출 횟수를 세어 두 번째(append 경로) 호출에서만 예외를 던지십시오.
  • tests/relay-dispatch/scripts/facts.test.js#L542-L565: fsModule 스텁 대신 repairTornTail이 제공하는 fault 콜백을 사용하고 journal_read 단계에서만 예외를 던지십시오.
🤖 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 `@tests/relay-dispatch/scripts/facts.test.js` around lines 481 - 514, Update
tests/relay-dispatch/scripts/facts.test.js lines 481-514 to count openSync calls
and inject the failure only on the second call, so the initial readFacts
preflight succeeds and the append open boundary is exercised. Update lines
542-565 to use repairTornTail’s fault callback instead of an fsModule stub,
throwing only for the journal_read stage; no other fault stages should be
affected.
tests/relay-dispatch/scripts/runtime-contract-blackbox.test.js (1)

8-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

topLevelTestNames의 상태 전이를 설명하는 주석을 추가하세요.

이 함수는 문자열과 주석을 건너뛰고, 중괄호 깊이와 이전 최상위 토큰으로 직접 테스트 등록을 판별합니다. 이 보수적 판별 규칙을 설명하지 않으면 이후 변경에서 가짜 anchor를 허용하거나 유효한 anchor를 거부할 수 있습니다.

🤖 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 `@tests/relay-dispatch/scripts/runtime-contract-blackbox.test.js` around lines
8 - 44, Add comments within topLevelTestNames explaining how string/comment
skipping, braceDepth tracking, and lastTopLevelToken transitions conservatively
identify valid top-level test registrations. Document the anchor requirements
and state transitions so future changes preserve rejection of false anchors and
acceptance of valid ones.

Source: Coding guidelines

🤖 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 `@tests/relay-dispatch/fixtures/fake-codex.js`:
- Around line 14-15: Update the control-input parsing in the fake Codex fixture
so empty or whitespace-only stdin continues to use the default controls, but
non-empty invalid JSON writes an error to stderr and exits with a non-zero
status instead of continuing with {}. Preserve valid JSON handling and the
existing delay_ms/empty behavior.

In `@tests/relay-dispatch/fixtures/runtime-contract-noop.js`:
- Around line 3-18: Update runtime-contract.test.js to compare the exact export
keys of productionRuntime and the injected runtime-contract-noop fixture, using
exactKeys(...) or equivalent. Ensure the test fails when either runtime adds,
removes, or renames an export, including keeping the no-op fixture synchronized
with the runtime contract.

In `@tests/relay-dispatch/fixtures/write-containment-executor.js`:
- Line 23: Update the temp-directory initialization in the proof setup to use
os.tmpdir() when process.env.TMPDIR is unset, ensuring path.join receives a
valid directory and the temp-write probe reports the actual containment result.
Import or reuse the existing os module in the fixture.

In `@tests/relay-dispatch/scripts/facts.test.js`:
- Around line 349-352: Replace the forged-capability assertion around appendFact
with a test that reuses the capability returned by acquireFactLock while
substituting run_dir with the value from another execution. Keep the shared
capability identity intact and assert that appendFact rejects this mismatched
execution context, so the “forged capabilities” test validates run_dir checking
rather than rejection of a cloned lock object.

In `@tests/relay-dispatch/scripts/inspect-recover-blackbox.test.js`:
- Line 609: Add explicit coverage near the “verification refuses dirty
reviewable bytes” test for special worktree entries: create Unix socket and FIFO
entries, run the verification flow, and assert each is rejected with
unsafe_worktree_entry. Reuse the existing socket/FIFO setup and assertion
helpers from inspect-recover-blackbox.test.js, preserving the expected behavior
for regular files.

In `@tests/skills-lint/scripts/test-directives.test.js`:
- Around line 39-41: Update literalNameBefore to locate the nearest call for
test, it, or describe rather than searching only for test(. Allow optional
whitespace before the opening parenthesis, and ensure the selected call is the
closest preceding direct call so literalNameBefore reports the correct name or
preserves the existing fail-closed error.
- Around line 37-47: Update literalNameBefore so escape restoration matches the
source literal encodings: make the newline pattern match one backslash followed
by n and the backslash pattern match two backslashes. Perform backslash
restoration once at the end, after quote and newline decoding, while preserving
the existing quote handling.

---

Nitpick comments:
In `@tests/relay-dispatch/fixtures/fake-codex.js`:
- Around line 7-21: fake-codex fixture의 제어 프로토콜을 코드에 의미 있는 주석으로 설명하세요. `-C`와
`-o`가 작업 디렉터리와 결과 경로를 지정하고, stdin JSON의 `delay_ms`가 지연을 제어하며 `empty`가 작업 디렉터리
변경은 유지한 채 결과 artifact 생성만 생략한다는 내용을 해당 파싱 및 분기 로직 주변에 명시하세요.

In `@tests/relay-dispatch/fixtures/write-containment-executor.js`:
- Around line 60-68: Update finish() so that when controls.proof_in_result ===
true and output is absent, the proof is still persisted to
worktree/containment-proof.json; retain writing proof to output when output is
provided and preserve the existing executor status message for the
non-proof-in-result path.

In `@tests/relay-dispatch/scripts/facts.test.js`:
- Around line 481-514: Update tests/relay-dispatch/scripts/facts.test.js lines
481-514 to count openSync calls and inject the failure only on the second call,
so the initial readFacts preflight succeeds and the append open boundary is
exercised. Update lines 542-565 to use repairTornTail’s fault callback instead
of an fsModule stub, throwing only for the journal_read stage; no other fault
stages should be affected.

In `@tests/relay-dispatch/scripts/runtime-contract-blackbox.test.js`:
- Around line 8-44: Add comments within topLevelTestNames explaining how
string/comment skipping, braceDepth tracking, and lastTopLevelToken transitions
conservatively identify valid top-level test registrations. Document the anchor
requirements and state transitions so future changes preserve rejection of false
anchors and acceptance of valid ones.

In `@tests/skills-lint/scripts/ci-relay-matrix.test.js`:
- Around line 105-124: Update runnerStep to document that comment-only lines
inside the run block are excluded from the extracted body, while commenting out
an executable line causes the exact EXPECTED_RUNNER_STEP comparison to fail;
retain the existing behavior. Also simplify the body-line slicing by removing
the unnecessary Math.min wrapper and pass stepIndent directly to slice.
- Around line 198-217: Update the weakened-workflow cases in the assert.throws
test around assertCiContract to associate each mutation with its specific
expected error-message pattern. Replace the broad shared
/runner|step|exact|array|zero|serialize|guarded/i matcher with per-case
assertions that distinguish failures such as runner, step, exact, array, zero,
serialize, and guarded contract violations.

In `@tests/skills-lint/scripts/test-directives.test.js`:
- Around line 216-275: Extend the injections array in the “directive guard
rejects injected unauthorized directives” test with options-object skip cases
for both it and describe, using the same skipProperty pattern as the existing
test case. Keep the cases unauthorized and ensure they exercise the
literalNameBefore handling for non-test callers.
- Around line 49-52: Add concise one- or two-line comments above regexStartsAt,
withoutComments, and stringMask describing each scanner’s purpose and the
strings, template literals, regex literals, and character classes it tracks. In
particular, document that the character sequence "([{:;,=!&|?+-*%"~" in
regexStartsAt is an ordinary string used for delimiter membership checks, not a
regular-expression character class.
🪄 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: 36723021-52ee-40cd-b376-d51147bcf733

📥 Commits

Reviewing files that changed from the base of the PR and between 3bb94c7 and 24121e5.

⛔ Files ignored due to path filters (2)
  • tests/ledger/vnext-baseline.generated.json is excluded by !**/*.generated.*
  • tests/ledger/vnext-test-sites.generated.json is excluded by !**/*.generated.*
📒 Files selected for processing (53)
  • CLAUDE.md
  • README.md
  • backlog/sprints/2026-08-relay-runtime-slimming.md
  • docs/contracts/relay-runtime-contracts.v1.json
  • docs/contracts/relay-runtime-inventory.v1.json
  • docs/ranked-deletion-inventory.md
  • docs/relay-operator-guide.md
  • docs/script-inventory-and-cleanup.md
  • docs/test-accounting-retirement-1196.md
  • references/architecture.md
  • skills/relay-dispatch/scripts/recover.js
  • tests/ledger/README.md
  • tests/ledger/vnext-baseline-measurements.json
  • tests/ledger/vnext-test-ledger.json
  • tests/relay-dispatch/fixtures/adapter-runtime-preload.js
  • tests/relay-dispatch/fixtures/fake-cline.js
  • tests/relay-dispatch/fixtures/fake-codex.js
  • tests/relay-dispatch/fixtures/fake-cursor.js
  • tests/relay-dispatch/fixtures/json-observer.js
  • tests/relay-dispatch/fixtures/runtime-contract-noop.js
  • tests/relay-dispatch/fixtures/vnext-shadow-parity-corpus.json
  • tests/relay-dispatch/fixtures/write-containment-executor.js
  • tests/relay-dispatch/scripts/dispatch.test.js
  • tests/relay-dispatch/scripts/docs-defaults.test.js
  • tests/relay-dispatch/scripts/facts.test.js
  • tests/relay-dispatch/scripts/host-concurrency.test.js
  • tests/relay-dispatch/scripts/inspect-recover-blackbox.test.js
  • tests/relay-dispatch/scripts/recover-safe-staging.test.js
  • tests/relay-dispatch/scripts/relay-recover-cli.test.js
  • tests/relay-dispatch/scripts/run-fold.test.js
  • tests/relay-dispatch/scripts/run-store.test.js
  • tests/relay-dispatch/scripts/runtime-contract-blackbox.test.js
  • tests/relay-dispatch/scripts/runtime-contract-noop.test.js
  • tests/relay-dispatch/scripts/runtime-contract.test.js
  • tests/relay-dispatch/scripts/toolset-mismatch.test.js
  • tests/relay-fleet/scripts/fleet-derived.test.js
  • tests/relay-merge/fixtures/merge-observer.js
  • tests/relay-merge/scripts/finalize-run.test.js
  • tests/relay-merge/scripts/gate-check.test.js
  • tests/relay-review/fixtures/fake-gh.js
  • tests/relay-review/scripts/review-runner.test.js
  • tests/relay/scripts/relay-status-recover.test.js
  • tests/relay/scripts/run-preflight.test.js
  • tests/skills-lint/scripts/ci-matrix-completeness.test.js
  • tests/skills-lint/scripts/ci-relay-matrix.test.js
  • tests/skills-lint/scripts/ci-test-coverage.test.js
  • tests/skills-lint/scripts/pr-view-json-contract.test.js
  • tests/skills-lint/scripts/skill-inputs-drift.test.js
  • tests/skills-lint/scripts/test-directives.test.js
  • tests/skills-lint/scripts/vnext-runtime-inventory.js
  • tests/skills-lint/scripts/vnext-runtime-inventory.test.js
  • tests/skills-lint/scripts/vnext-test-ledger.js
  • tests/skills-lint/scripts/vnext-test-ledger.test.js
💤 Files with no reviewable changes (12)
  • tests/ledger/README.md
  • tests/skills-lint/scripts/ci-test-coverage.test.js
  • tests/skills-lint/scripts/ci-matrix-completeness.test.js
  • tests/ledger/vnext-test-ledger.json
  • tests/skills-lint/scripts/vnext-runtime-inventory.test.js
  • tests/relay-dispatch/fixtures/vnext-shadow-parity-corpus.json
  • tests/skills-lint/scripts/vnext-test-ledger.js
  • tests/skills-lint/scripts/vnext-test-ledger.test.js
  • tests/skills-lint/scripts/vnext-runtime-inventory.js
  • skills/relay-dispatch/scripts/recover.js
  • docs/contracts/relay-runtime-inventory.v1.json
  • tests/ledger/vnext-baseline-measurements.json

});

test("verification refuses dirty reviewable bytes and special entries classify without reading", () => {
test("verification refuses dirty reviewable bytes", () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 특수 파일 분류 로직이 프로덕션에 남아 있는지, 남은 테스트가 있는지 확인
set -uo pipefail

echo "== assertCleanVerificationObservation 구현"
rg -n -C 20 'assertCleanVerificationObservation' skills || true

echo "== 특수 파일 / socket 분류 참조"
rg -n --type=js -C 4 'isSocket|isFIFO|isBlockDevice|isCharacterDevice|special' skills || true

echo "== 남은 socket 관련 테스트"
rg -n --type=js -C 3 'isSocket|unix socket|mkfifo' tests || true

Repository: sungjunlee/dev-relay

Length of output: 19976


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== recover.js unsafeWorktreeEntries implementation"
sed -n '520,590p' skills/relay-dispatch/scripts/recover.js | cat -n

echo "== inspect-recover-blackblack relevant tests around line 609"
sed -n '560,635p' tests/relay-dispatch/scripts/inspect-recover-blackbox.test.js | cat -n

echo "== diff summary"
git diff --stat || true

Repository: sungjunlee/dev-relay

Length of output: 7083


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== locate and inspect unsafeWorktreeEntries definition"
rg -n 'function unsafeWorktreeEntries|const unsafeWorktreeEntries|unsafeWorktreeEntries' skills/relay-dispatch/scripts/recover.js -C 80

echo "== tests around removed section"
rg -n -C 12 'unix socket|special entry|ignored|ignored entry|assertCleanVerificationObservation|special entries' tests/relay-dispatch/scripts/inspect-recover-blackbox.test.js || true

Repository: sungjunlee/dev-relay

Length of output: 28216


특수 파일 거부 경로에 대한 새 테스트를 추가하십시오.

unsafeWorktreeEntries()fs.lstatSync()에서 isSymbolicLink() || !entry.isFile()로 reject하므로, Unix socket, FIFO, device 파일 같은 특수 파일은 아직 unsafe_worktree_entry로 막힙니다. 하지만 inspect-recover-blackbox.test.js의 socket/FIFO 관련 테스트가 제거되면 이 분기는 새로운 테스트로 보호되지 않습니다. 삭제된 테스트 대신 socket과 FIFO case를 명시적으로 커버하는 테스트를 추가하십시오.

🤖 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 `@tests/relay-dispatch/scripts/inspect-recover-blackbox.test.js` at line 609,
Add explicit coverage near the “verification refuses dirty reviewable bytes”
test for special worktree entries: create Unix socket and FIFO entries, run the
verification flow, and assert each is rejected with unsafe_worktree_entry. Reuse
the existing socket/FIFO setup and assertion helpers from
inspect-recover-blackbox.test.js, preserving the expected behavior for regular
files.

Comment on lines +37 to +47
function literalNameBefore(source, offset) {
const prefix = source.slice(0, offset);
const call = prefix.lastIndexOf("test(");
if (call < 0) throw new Error("skip directive is not attached to a literal test call");
const argument = prefix.slice(call + 5).trimStart();
const match = /^("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\$]|\$(?!\{))*`)/.exec(argument);
if (!match) throw new Error("skip directive test name must be a normalized literal");
const quote = match[1][0];
const body = match[1].slice(1, -1);
return body.replace(new RegExp(`\\\\${quote}`, "g"), quote).replace(/\\\\n/g, "\n").replace(/\\\\\\\\/g, "\\");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

리터럴 이스케이프 해제 규칙이 한 단계 과도하게 이스케이프되었습니다.

Line 46의 첫 번째 replace는 정규식 패턴을 문자열로 만들기 때문에 \" 한 개의 백슬래시를 정확히 처리합니다. 그러나 뒤의 두 정규식 리터럴은 백슬래시가 두 배입니다.

  • /\\\\n/\\n(백슬래시 2개 + n)과 일치합니다. 소스 리터럴에 실제로 들어 있는 \n(백슬래시 1개 + n)과는 일치하지 않습니다.
  • /\\\\\\\\/는 백슬래시 4개와 일치합니다. 소스 리터럴의 \\(백슬래시 2개)와는 일치하지 않습니다.

그래서 테스트 이름에 \n 또는 \\ 이스케이프가 들어가면 복원된 이름이 ALLOWED_SKIPS 항목과 달라집니다. 현재 허용 목록에는 이스케이프가 없어 즉시 실패하지는 않습니다. 향후 이름 추가 시 잘못된 불일치가 발생합니다.

또한 백슬래시 복원은 마지막에 한 번만 수행하는 순서가 안전합니다.

🐛 제안 수정
-  return body.replace(new RegExp(`\\\\${quote}`, "g"), quote).replace(/\\\\n/g, "\n").replace(/\\\\\\\\/g, "\\");
+  return body.replace(/\\(.)/g, (_, character) => {
+    if (character === "n") return "\n";
+    if (character === "t") return "\t";
+    return character;
+  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function literalNameBefore(source, offset) {
const prefix = source.slice(0, offset);
const call = prefix.lastIndexOf("test(");
if (call < 0) throw new Error("skip directive is not attached to a literal test call");
const argument = prefix.slice(call + 5).trimStart();
const match = /^("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\$]|\$(?!\{))*`)/.exec(argument);
if (!match) throw new Error("skip directive test name must be a normalized literal");
const quote = match[1][0];
const body = match[1].slice(1, -1);
return body.replace(new RegExp(`\\\\${quote}`, "g"), quote).replace(/\\\\n/g, "\n").replace(/\\\\\\\\/g, "\\");
}
function literalNameBefore(source, offset) {
const prefix = source.slice(0, offset);
const call = prefix.lastIndexOf("test(");
if (call < 0) throw new Error("skip directive is not attached to a literal test call");
const argument = prefix.slice(call + 5).trimStart();
const match = /^("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\$]|\$(?!\{))*`)/.exec(argument);
if (!match) throw new Error("skip directive test name must be a normalized literal");
const quote = match[1][0];
const body = match[1].slice(1, -1);
return body.replace(/\\(.)/g, (_, character) => {
if (character === "n") return "\n";
if (character === "t") return "\t";
return character;
});
}
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 45-45: Detects non-literal values in regular expressions
Context: new RegExp(\\\\${quote}, "g")
Note: [CWE-1333] Inefficient Regular Expression Complexity (ReDoS via non-literal RegExp).

(detect-non-literal-regexp)

🪛 OpenGrep (1.26.0)

[ERROR] 42-42: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 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 `@tests/skills-lint/scripts/test-directives.test.js` around lines 37 - 47,
Update literalNameBefore so escape restoration matches the source literal
encodings: make the newline pattern match one backslash followed by n and the
backslash pattern match two backslashes. Perform backslash restoration once at
the end, after quote and newline decoding, while preserving the existing quote
handling.

Comment on lines +39 to +41
const call = prefix.lastIndexOf("test(");
if (call < 0) throw new Error("skip directive is not attached to a literal test call");
const argument = prefix.slice(call + 5).trimStart();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

literalNameBeforeitdescribe 호출을 인식하지 못합니다.

Line 39는 test(만 역방향 검색합니다. directCallOptionstest, it, describe를 모두 수집합니다. 따라서 it('x', { skip: true }, ...) 형태에서는 앞쪽의 다른 test( 호출 이름이 잘못 추출되거나 예외가 발생합니다. 또한 test ( 처럼 공백이 있는 호출도 놓칩니다.

현재 동작은 대체로 fail-closed입니다. 그래도 진단 메시지가 잘못된 이름을 가리킬 수 있습니다. 세 이름과 공백을 모두 인식하도록 검색을 수정하세요.

🐛 제안 수정
 function literalNameBefore(source, offset) {
   const prefix = source.slice(0, offset);
-  const call = prefix.lastIndexOf("test(");
-  if (call < 0) throw new Error("skip directive is not attached to a literal test call");
-  const argument = prefix.slice(call + 5).trimStart();
+  const calls = [...prefix.matchAll(/\b(?:test|it|describe)\s*\(/g)];
+  const call = calls.at(-1);
+  if (!call) throw new Error("skip directive is not attached to a literal test call");
+  const argument = prefix.slice(call.index + call[0].length).trimStart();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const call = prefix.lastIndexOf("test(");
if (call < 0) throw new Error("skip directive is not attached to a literal test call");
const argument = prefix.slice(call + 5).trimStart();
function literalNameBefore(source, offset) {
const prefix = source.slice(0, offset);
const calls = [...prefix.matchAll(/\b(?:test|it|describe)\s*\(/g)];
const call = calls.at(-1);
if (!call) throw new Error("skip directive is not attached to a literal test call");
const argument = prefix.slice(call.index + call[0].length).trimStart();
🤖 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 `@tests/skills-lint/scripts/test-directives.test.js` around lines 39 - 41,
Update literalNameBefore to locate the nearest call for test, it, or describe
rather than searching only for test(. Allow optional whitespace before the
opening parenthesis, and ensure the selected call is the closest preceding
direct call so literalNameBefore reports the correct name or preserves the
existing fail-closed error.

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 7

🧹 Nitpick comments (8)
tests/skills-lint/scripts/test-directives.test.js (2)

216-275: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

itdescribe의 옵션 객체 skip 케이스를 주입 목록에 추가하세요.

Line 229부터의 주입 목록은 .skip 멤버 형태에서만 itdescribe를 다룹니다. 옵션 객체 형태(it('x', { skip: true }, ...))는 없습니다. 이 형태가 literalNameBeforetest( 전용 검색과 만나는 경로가 검증되지 않습니다. 해당 케이스를 추가하세요.

♻️ 제안 변경
     "test('hidden', { [`skip`]: true }, () => {});",
+    `it('hidden', { ${skipProperty} }, () => {});`,
+    `describe('hidden', { ${skipProperty} }, () => {});`,
🤖 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 `@tests/skills-lint/scripts/test-directives.test.js` around lines 216 - 275,
Extend the injections array in the “directive guard rejects injected
unauthorized directives” test with options-object skip cases for both it and
describe, using the same skipProperty pattern as the existing test case. Keep
the cases unauthorized and ensure they exercise the literalNameBefore handling
for non-test callers.

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

수동 스캐너에 짧은 설명 주석을 추가하세요.

regexStartsAt, withoutComments, stringMask는 문자열, 템플릿 리터럴, 정규식 리터럴, 문자 클래스를 직접 추적합니다. 각 함수의 목적과 처리 대상을 한두 줄로 명시하세요. 복잡한 로직에는 의미 있는 주석을 사용해야 합니다.

특히 Line 51의 "([{:;,=!&|?+-*%~"는 문자 클래스가 아니라 일반 문자열입니다. 이 점을 주석으로 밝히면 오해를 막을 수 있습니다.

Also applies to: 54-87, 89-117

🤖 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 `@tests/skills-lint/scripts/test-directives.test.js` around lines 49 - 52, Add
concise one- or two-line comments above regexStartsAt, withoutComments, and
stringMask describing each scanner’s purpose and the strings, template literals,
regex literals, and character classes it tracks. In particular, document that
the character sequence "([{:;,=!&|?+-*%"~" in regexStartsAt is an ordinary
string used for delimiter membership checks, not a regular-expression character
class.

Source: Coding guidelines

tests/skills-lint/scripts/ci-relay-matrix.test.js (2)

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

runnerStep의 주석 제거 동작을 문서화하세요.

runnerSteprun 블록 안의 주석 줄을 본문에서 제외합니다. 이 동작 때문에 주석이 삽입되어도 EXPECTED_RUNNER_STEP 비교는 통과합니다. 반대로 실행 줄을 주석으로 바꾸면 해당 줄이 사라져서 비교가 실패합니다. 이 의도를 짧은 주석으로 명시하세요. 복잡한 로직에는 의미 있는 주석을 사용해야 합니다.

또한 Line 115의 Math.min은 불필요합니다. String.prototype.slice는 인덱스를 길이로 자동 제한합니다.

♻️ 제안 변경
   const stepIndent = indent(steps[0].line);
   const body = [];
+  // Comment lines are dropped from the body. Commenting out an executable
+  // line therefore removes it and fails the exact-match comparison.
   for (let index = steps[0].index + 1; index < lines.length; index += 1) {
     if (lines[index].trim() && indent(lines[index]) <= stepIndent) break;
     if (lines[index].trim() && !lines[index].trimStart().startsWith("#")) {
-      body.push(lines[index].slice(Math.min(lines[index].length, stepIndent)));
+      body.push(lines[index].slice(stepIndent));
     }
   }
🤖 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 `@tests/skills-lint/scripts/ci-relay-matrix.test.js` around lines 105 - 124,
Update runnerStep to document that comment-only lines inside the run block are
excluded from the extracted body, while commenting out an executable line causes
the exact EXPECTED_RUNNER_STEP comparison to fail; retain the existing behavior.
Also simplify the body-line slicing by removing the unnecessary Math.min wrapper
and pass stepIndent directly to slice.

Source: Coding guidelines


198-217: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

약화 케이스의 오류 메시지 검증을 좁히세요.

Line 216의 정규식 /runner|step|exact|array|zero|serialize|guarded/i는 이 파일의 거의 모든 단언 메시지와 일치합니다. 예를 들어 잡 봉투 실패 메시지와 러너 실패 메시지가 서로 구분되지 않습니다. 그래서 케이스가 의도와 다른 이유로 실패해도 테스트는 통과합니다. 각 약화 케이스에 기대 메시지를 함께 지정하세요.

♻️ 제안 변경
-  for (const weakened of [
-    value.workflow.replace("files=()", ""),
-    ...
-  ]) assert.throws(() => assertCiContract({ testsDir: value.root, workflow: weakened }), /runner|step|exact|array|zero|serialize|guarded/i);
+  for (const [weakened, expected] of [
+    [value.workflow.replace("files=()", ""), /fail-closed shell program/],
+    [value.workflow.replace("    strategy:", "    if: always()\n    strategy:"), /job-level policy envelope/],
+    // ... 각 케이스에 해당 메시지를 지정
+  ]) assert.throws(() => assertCiContract({ testsDir: value.root, workflow: weakened }), expected);
🤖 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 `@tests/skills-lint/scripts/ci-relay-matrix.test.js` around lines 198 - 217,
Update the weakened-workflow cases in the assert.throws test around
assertCiContract to associate each mutation with its specific expected
error-message pattern. Replace the broad shared
/runner|step|exact|array|zero|serialize|guarded/i matcher with per-case
assertions that distinguish failures such as runner, step, exact, array, zero,
serialize, and guarded contract violations.
tests/relay-dispatch/fixtures/fake-codex.js (1)

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

fixture 제어 프로토콜을 주석으로 설명하세요.

-C, -o, stdin JSON, delay_ms, empty가 각각 작업 디렉터리, 결과 경로, 지연, 결과 파일 생성을 제어합니다. 이 의미가 호출부만으로는 명확하지 않습니다. 특히 empty가 작업 디렉터리 변경은 남기고 결과 artifact만 생략한다는 의도를 기록하세요.

주석 추가 예시
 const args = process.argv.slice(2);
+// The harness passes the working directory and result path as argv values.
 const value = (flag) => {
...
 const output = value("-o");
+// stdin controls delay injection and omission of the result artifact.
 let controls = {};
...
 if (controls.empty === true) process.exit(0);

As per coding guidelines, 복잡한 로직에는 의미 있는 주석을 추가해야 합니다.

🤖 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 `@tests/relay-dispatch/fixtures/fake-codex.js` around lines 7 - 21, fake-codex
fixture의 제어 프로토콜을 코드에 의미 있는 주석으로 설명하세요. `-C`와 `-o`가 작업 디렉터리와 결과 경로를 지정하고, stdin
JSON의 `delay_ms`가 지연을 제어하며 `empty`가 작업 디렉터리 변경은 유지한 채 결과 artifact 생성만 생략한다는 내용을
해당 파싱 및 분기 로직 주변에 명시하세요.

Source: Coding guidelines

tests/relay-dispatch/fixtures/write-containment-executor.js (1)

60-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

proof_in_result가 참이고 -o가 없으면 증거가 사라집니다.

63-64행은 controls.proof_in_result === true일 때 output이 있을 때만 기록합니다. output이 null이면 proof가 어디에도 저장되지 않습니다. 테스트는 증거 없이 실패하고 원인을 알기 어렵습니다. 이 조합에서는 worktree 내 containment-proof.json으로 되돌리거나 명시적으로 종료 코드를 설정하십시오.

♻️ 제안 수정
   if (controls.proof_in_result === true) {
-    if (output) fs.writeFileSync(output, bytes);
+    if (output) fs.writeFileSync(output, bytes);
+    else fs.writeFileSync(path.join(worktree, "containment-proof.json"), bytes);
   } else {
🤖 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 `@tests/relay-dispatch/fixtures/write-containment-executor.js` around lines 60
- 68, Update finish() so that when controls.proof_in_result === true and output
is absent, the proof is still persisted to worktree/containment-proof.json;
retain writing proof to output when output is provided and preserve the existing
executor status message for the non-proof-in-result path.
tests/relay-dispatch/scripts/facts.test.js (1)

481-514: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

주입된 fs 실패가 의도한 경계보다 먼저 발생합니다. appendFactrepairTornTail은 모두 본 작업 전에 readFacts({ eventsPath, fsModule })를 호출합니다. 두 테스트의 스텁은 해당 fs 메서드의 모든 호출에서 예외를 던집니다. 그러므로 예외는 항상 사전 판독 단계에서 발생하고, 테스트 이름이 지목하는 append open 경계와 repair journal-read 경계는 실행되지 않습니다. 두 테스트는 통과하지만 대상 코드를 검사하지 않습니다.

  • tests/relay-dispatch/scripts/facts.test.js#L481-L514: openSync 호출 횟수를 세어 두 번째(append 경로) 호출에서만 예외를 던지십시오.
  • tests/relay-dispatch/scripts/facts.test.js#L542-L565: fsModule 스텁 대신 repairTornTail이 제공하는 fault 콜백을 사용하고 journal_read 단계에서만 예외를 던지십시오.
🤖 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 `@tests/relay-dispatch/scripts/facts.test.js` around lines 481 - 514, Update
tests/relay-dispatch/scripts/facts.test.js lines 481-514 to count openSync calls
and inject the failure only on the second call, so the initial readFacts
preflight succeeds and the append open boundary is exercised. Update lines
542-565 to use repairTornTail’s fault callback instead of an fsModule stub,
throwing only for the journal_read stage; no other fault stages should be
affected.
tests/relay-dispatch/scripts/runtime-contract-blackbox.test.js (1)

8-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

topLevelTestNames의 상태 전이를 설명하는 주석을 추가하세요.

이 함수는 문자열과 주석을 건너뛰고, 중괄호 깊이와 이전 최상위 토큰으로 직접 테스트 등록을 판별합니다. 이 보수적 판별 규칙을 설명하지 않으면 이후 변경에서 가짜 anchor를 허용하거나 유효한 anchor를 거부할 수 있습니다.

🤖 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 `@tests/relay-dispatch/scripts/runtime-contract-blackbox.test.js` around lines
8 - 44, Add comments within topLevelTestNames explaining how string/comment
skipping, braceDepth tracking, and lastTopLevelToken transitions conservatively
identify valid top-level test registrations. Document the anchor requirements
and state transitions so future changes preserve rejection of false anchors and
acceptance of valid ones.

Source: Coding guidelines

🤖 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 `@tests/relay-dispatch/fixtures/fake-codex.js`:
- Around line 14-15: Update the control-input parsing in the fake Codex fixture
so empty or whitespace-only stdin continues to use the default controls, but
non-empty invalid JSON writes an error to stderr and exits with a non-zero
status instead of continuing with {}. Preserve valid JSON handling and the
existing delay_ms/empty behavior.

In `@tests/relay-dispatch/fixtures/runtime-contract-noop.js`:
- Around line 3-18: Update runtime-contract.test.js to compare the exact export
keys of productionRuntime and the injected runtime-contract-noop fixture, using
exactKeys(...) or equivalent. Ensure the test fails when either runtime adds,
removes, or renames an export, including keeping the no-op fixture synchronized
with the runtime contract.

In `@tests/relay-dispatch/fixtures/write-containment-executor.js`:
- Line 23: Update the temp-directory initialization in the proof setup to use
os.tmpdir() when process.env.TMPDIR is unset, ensuring path.join receives a
valid directory and the temp-write probe reports the actual containment result.
Import or reuse the existing os module in the fixture.

In `@tests/relay-dispatch/scripts/facts.test.js`:
- Around line 349-352: Replace the forged-capability assertion around appendFact
with a test that reuses the capability returned by acquireFactLock while
substituting run_dir with the value from another execution. Keep the shared
capability identity intact and assert that appendFact rejects this mismatched
execution context, so the “forged capabilities” test validates run_dir checking
rather than rejection of a cloned lock object.

In `@tests/relay-dispatch/scripts/inspect-recover-blackbox.test.js`:
- Line 609: Add explicit coverage near the “verification refuses dirty
reviewable bytes” test for special worktree entries: create Unix socket and FIFO
entries, run the verification flow, and assert each is rejected with
unsafe_worktree_entry. Reuse the existing socket/FIFO setup and assertion
helpers from inspect-recover-blackbox.test.js, preserving the expected behavior
for regular files.

In `@tests/skills-lint/scripts/test-directives.test.js`:
- Around line 39-41: Update literalNameBefore to locate the nearest call for
test, it, or describe rather than searching only for test(. Allow optional
whitespace before the opening parenthesis, and ensure the selected call is the
closest preceding direct call so literalNameBefore reports the correct name or
preserves the existing fail-closed error.
- Around line 37-47: Update literalNameBefore so escape restoration matches the
source literal encodings: make the newline pattern match one backslash followed
by n and the backslash pattern match two backslashes. Perform backslash
restoration once at the end, after quote and newline decoding, while preserving
the existing quote handling.

---

Nitpick comments:
In `@tests/relay-dispatch/fixtures/fake-codex.js`:
- Around line 7-21: fake-codex fixture의 제어 프로토콜을 코드에 의미 있는 주석으로 설명하세요. `-C`와
`-o`가 작업 디렉터리와 결과 경로를 지정하고, stdin JSON의 `delay_ms`가 지연을 제어하며 `empty`가 작업 디렉터리
변경은 유지한 채 결과 artifact 생성만 생략한다는 내용을 해당 파싱 및 분기 로직 주변에 명시하세요.

In `@tests/relay-dispatch/fixtures/write-containment-executor.js`:
- Around line 60-68: Update finish() so that when controls.proof_in_result ===
true and output is absent, the proof is still persisted to
worktree/containment-proof.json; retain writing proof to output when output is
provided and preserve the existing executor status message for the
non-proof-in-result path.

In `@tests/relay-dispatch/scripts/facts.test.js`:
- Around line 481-514: Update tests/relay-dispatch/scripts/facts.test.js lines
481-514 to count openSync calls and inject the failure only on the second call,
so the initial readFacts preflight succeeds and the append open boundary is
exercised. Update lines 542-565 to use repairTornTail’s fault callback instead
of an fsModule stub, throwing only for the journal_read stage; no other fault
stages should be affected.

In `@tests/relay-dispatch/scripts/runtime-contract-blackbox.test.js`:
- Around line 8-44: Add comments within topLevelTestNames explaining how
string/comment skipping, braceDepth tracking, and lastTopLevelToken transitions
conservatively identify valid top-level test registrations. Document the anchor
requirements and state transitions so future changes preserve rejection of false
anchors and acceptance of valid ones.

In `@tests/skills-lint/scripts/ci-relay-matrix.test.js`:
- Around line 105-124: Update runnerStep to document that comment-only lines
inside the run block are excluded from the extracted body, while commenting out
an executable line causes the exact EXPECTED_RUNNER_STEP comparison to fail;
retain the existing behavior. Also simplify the body-line slicing by removing
the unnecessary Math.min wrapper and pass stepIndent directly to slice.
- Around line 198-217: Update the weakened-workflow cases in the assert.throws
test around assertCiContract to associate each mutation with its specific
expected error-message pattern. Replace the broad shared
/runner|step|exact|array|zero|serialize|guarded/i matcher with per-case
assertions that distinguish failures such as runner, step, exact, array, zero,
serialize, and guarded contract violations.

In `@tests/skills-lint/scripts/test-directives.test.js`:
- Around line 216-275: Extend the injections array in the “directive guard
rejects injected unauthorized directives” test with options-object skip cases
for both it and describe, using the same skipProperty pattern as the existing
test case. Keep the cases unauthorized and ensure they exercise the
literalNameBefore handling for non-test callers.
- Around line 49-52: Add concise one- or two-line comments above regexStartsAt,
withoutComments, and stringMask describing each scanner’s purpose and the
strings, template literals, regex literals, and character classes it tracks. In
particular, document that the character sequence "([{:;,=!&|?+-*%"~" in
regexStartsAt is an ordinary string used for delimiter membership checks, not a
regular-expression character class.
🪄 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: 36723021-52ee-40cd-b376-d51147bcf733

📥 Commits

Reviewing files that changed from the base of the PR and between 3bb94c7 and 24121e5.

⛔ Files ignored due to path filters (2)
  • tests/ledger/vnext-baseline.generated.json is excluded by !**/*.generated.*
  • tests/ledger/vnext-test-sites.generated.json is excluded by !**/*.generated.*
📒 Files selected for processing (53)
  • CLAUDE.md
  • README.md
  • backlog/sprints/2026-08-relay-runtime-slimming.md
  • docs/contracts/relay-runtime-contracts.v1.json
  • docs/contracts/relay-runtime-inventory.v1.json
  • docs/ranked-deletion-inventory.md
  • docs/relay-operator-guide.md
  • docs/script-inventory-and-cleanup.md
  • docs/test-accounting-retirement-1196.md
  • references/architecture.md
  • skills/relay-dispatch/scripts/recover.js
  • tests/ledger/README.md
  • tests/ledger/vnext-baseline-measurements.json
  • tests/ledger/vnext-test-ledger.json
  • tests/relay-dispatch/fixtures/adapter-runtime-preload.js
  • tests/relay-dispatch/fixtures/fake-cline.js
  • tests/relay-dispatch/fixtures/fake-codex.js
  • tests/relay-dispatch/fixtures/fake-cursor.js
  • tests/relay-dispatch/fixtures/json-observer.js
  • tests/relay-dispatch/fixtures/runtime-contract-noop.js
  • tests/relay-dispatch/fixtures/vnext-shadow-parity-corpus.json
  • tests/relay-dispatch/fixtures/write-containment-executor.js
  • tests/relay-dispatch/scripts/dispatch.test.js
  • tests/relay-dispatch/scripts/docs-defaults.test.js
  • tests/relay-dispatch/scripts/facts.test.js
  • tests/relay-dispatch/scripts/host-concurrency.test.js
  • tests/relay-dispatch/scripts/inspect-recover-blackbox.test.js
  • tests/relay-dispatch/scripts/recover-safe-staging.test.js
  • tests/relay-dispatch/scripts/relay-recover-cli.test.js
  • tests/relay-dispatch/scripts/run-fold.test.js
  • tests/relay-dispatch/scripts/run-store.test.js
  • tests/relay-dispatch/scripts/runtime-contract-blackbox.test.js
  • tests/relay-dispatch/scripts/runtime-contract-noop.test.js
  • tests/relay-dispatch/scripts/runtime-contract.test.js
  • tests/relay-dispatch/scripts/toolset-mismatch.test.js
  • tests/relay-fleet/scripts/fleet-derived.test.js
  • tests/relay-merge/fixtures/merge-observer.js
  • tests/relay-merge/scripts/finalize-run.test.js
  • tests/relay-merge/scripts/gate-check.test.js
  • tests/relay-review/fixtures/fake-gh.js
  • tests/relay-review/scripts/review-runner.test.js
  • tests/relay/scripts/relay-status-recover.test.js
  • tests/relay/scripts/run-preflight.test.js
  • tests/skills-lint/scripts/ci-matrix-completeness.test.js
  • tests/skills-lint/scripts/ci-relay-matrix.test.js
  • tests/skills-lint/scripts/ci-test-coverage.test.js
  • tests/skills-lint/scripts/pr-view-json-contract.test.js
  • tests/skills-lint/scripts/skill-inputs-drift.test.js
  • tests/skills-lint/scripts/test-directives.test.js
  • tests/skills-lint/scripts/vnext-runtime-inventory.js
  • tests/skills-lint/scripts/vnext-runtime-inventory.test.js
  • tests/skills-lint/scripts/vnext-test-ledger.js
  • tests/skills-lint/scripts/vnext-test-ledger.test.js
💤 Files with no reviewable changes (12)
  • tests/ledger/README.md
  • tests/skills-lint/scripts/ci-test-coverage.test.js
  • tests/skills-lint/scripts/ci-matrix-completeness.test.js
  • tests/ledger/vnext-test-ledger.json
  • tests/skills-lint/scripts/vnext-runtime-inventory.test.js
  • tests/relay-dispatch/fixtures/vnext-shadow-parity-corpus.json
  • tests/skills-lint/scripts/vnext-test-ledger.js
  • tests/skills-lint/scripts/vnext-test-ledger.test.js
  • tests/skills-lint/scripts/vnext-runtime-inventory.js
  • skills/relay-dispatch/scripts/recover.js
  • docs/contracts/relay-runtime-inventory.v1.json
  • tests/ledger/vnext-baseline-measurements.json
🛑 Comments failed to post (4)
tests/relay-dispatch/fixtures/fake-codex.js (1)

14-15: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

잘못된 제어 JSON을 조용히 기본값으로 처리하지 마세요.

비어 있지 않은 stdin이 잘못된 JSON이면 catch {}controls = {} 상태로 계속 실행합니다. 그러면 fixture가 delay_ms 또는 empty를 무시하고 정상 완료하여 테스트가 잘못 통과할 수 있습니다. 빈 stdin만 기본값으로 허용하고, 비어 있지 않은 파싱 오류는 stderr와 비정상 종료로 처리하세요.

수정 예시
-let controls = {};
-try { controls = JSON.parse(fs.readFileSync(0, "utf8")); } catch {}
+const controlText = fs.readFileSync(0, "utf8").trim();
+let controls = {};
+if (controlText) {
+  try {
+    controls = JSON.parse(controlText);
+  } catch (error) {
+    process.stderr.write(`invalid control JSON: ${error.message}\n`);
+    process.exit(65);
+  }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

const controlText = fs.readFileSync(0, "utf8").trim();
let controls = {};
if (controlText) {
  try {
    controls = JSON.parse(controlText);
  } catch (error) {
    process.stderr.write(`invalid control JSON: ${error.message}\n`);
    process.exit(65);
  }
}
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 14-14: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.readFileSync(0, "utf8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

🤖 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 `@tests/relay-dispatch/fixtures/fake-codex.js` around lines 14 - 15, Update the
control-input parsing in the fake Codex fixture so empty or whitespace-only
stdin continues to use the default controls, but non-empty invalid JSON writes
an error to stderr and exits with a non-zero status instead of continuing with
{}. Preserve valid JSON handling and the existing delay_ms/empty behavior.
tests/relay-dispatch/fixtures/runtime-contract-noop.js (1)

3-18: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# no-op 픽스처 키 집합이 실제 런타임 계약 export와 일치하는지 확인
set -uo pipefail

echo "== 픽스처 소비 지점"
rg -n -C 6 'runtime-contract-noop' tests skills || true

echo "== 런타임 계약 모듈 후보 탐색"
rg -n --type=js -C 2 '\brevalidateExternalFacts\b' skills || true

echo "== 후보 모듈의 export 구조"
fd -t f -e js . skills/relay-dispatch/scripts --exec sh -c 'rg -lq "revalidateExternalFacts" "$1" && ast-grep outline "$1" --items all' _ {}

Repository: sungjunlee/dev-relay

Length of output: 11857


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== scripts/facts.js exports and exactKeys contract tests =="
sed -n '700,760p' skills/relay-dispatch/scripts/facts.js
echo
rg -n 'exactKeys\(factsModule|runtime-contract-noop|runtime-contract.test.js|runtime-contract-noop.test.js' tests/relay-dispatch/scripts -C 4 || true
echo
sed -n '1,140p' tests/relay-dispatch/scripts/runtime-contract-noop.test.js
echo
sed -n '1,200p' tests/relay-dispatch/scripts/runtime-contract.test.js

echo "== JavaScript structural verifier =="
node - <<'JS'
const fs = require('fs');
const path = require('path');
const root = process.cwd();

const noopPath = path.join(root, 'tests/relay-dispatch/fixtures/runtime-contract-noop.js');
const factsPath = path.join(root, 'skills/relay-dispatch/scripts/facts.js');
const testPath = path.join(root, 'tests/relay-dispatch/scripts/runtime-contract-noop.test.js');
const runnerPath = path.join('/tmp/coderabbit-shell-logs/shell-output-az11Qv'); // not useful here; use above.

function trimComments(script) {
  return script
    .replace(/\/\/[^\n]*/g, '')
    .replace(/\/\*[\s\S]*?\*\//g, '');
}

const noop = require(noopPath);
const noopKeys = new Set(Object.keys(noop));

const factsSource = fs.readFileSync(factsPath, 'utf8');
const trimmed = trimComments(factsSource);
const lastExportMatch = [...trimmed.matchAll(/\bmodule\.exports\s*=\s*\{([^{]*?)\}/g)].pop();
if (!lastExportMatch) throw new Error('facts.js last module.exports object not found');
const exportsObjectText = '{' + lastExportMatch[1] + '}';
let exported;
function evalSafe() {
  const sandbox = {};
  eval('sandbox.exports = ' + exportsObjectText);
  return sandbox.exports;
}
try {
  exported = evalSafe();
} catch (e) {
  console.error('eval parse failed:', e.message);
  const keys = exportsObjectText.match(/\b([A-Za-z_$][\w$]*)\s*:/g) || [];
  exported = Object.fromEntries(keys.map(k => [k.match(/\b([A-Za-z_$][\w$]*)/)[1], true]));
}
const exportedKeys = new Set(Object.keys(exported));

function parseRequireExactKeys() {
  const text = fs.readFileSync(testPath, 'utf8');
  const parts = text.split('replaceModuleWithRequire');
  if (!['runtime-contract-noop'].every(k => parts.includes(k))) {
    return null;
  }
  const replacePart = parts[1].split('\nmodule.exports')[0].split('function replaceModuleWithRequire')[1] || '';
  const all = replacePart + parts[parts.length - 1];
  return all;
}

const requireText = parseRequireExactKeys();
console.log(JSON.stringify({
  noopKeys: Array.from(noopKeys).sort().join(','),
  exportedKeys: Array.from(exportedKeys).sort().join(','),
  noopMissingFromExported: Array.from(noopKeys.difference(exportedKeys)).sort(),
  exportedMissingFromNoop: Array.from(exportedKeys.difference(noopKeys)).sort(),
  exactKeysDetected: /exactKeys\s*\([^)]*factsModule/.test(testPath.includes?) || (function(){
    try { return requireText && /exactKeys\s*\([^)*?factsModule[^)]*\)?:[\s\S]{1,1000}/.test(requireText); } catch { return false; }
  })(),
}, null, 2));

function tryEvalRequire() {
  const vm = require('vm');
  const vmPath = fs.readFileSync('/tmp/vm-wrapper-1.js', 'utf-8').trim();
  fs.writeFileSync(vmPath, '');
  vm.runInNewContext(requireText, { exports: noop, exactKeys: () => true });
}
const vmPath = '/tmp/vm-wrapper-1.js';
fs.writeFileSync(vmPath, '');
try{tryEvalRequire()}catch(e){console.error('requireText eval error:', e.message)}
JS

Repository: sungjunlee/dev-relay

Length of output: 14723


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== runtime-contract-noop test relevant source =="
cat -n tests/relay-dispatch/scripts/runtime-contract-noop.test.js | sed -n '1,180p'

echo "== runtime-contract test exactKeys usages =="
rg -n -C 8 'exactKeys|createModuleWithRequires|replaceModuleWithRequires|runtime-contract-noop' tests/relay-dispatch/scripts/runtime-contract.test.js || true

echo "== facts.js export block =="
cat -n skills/relay-dispatch/scripts/facts.js | sed -n '724,740p'

echo "== deterministic key equality check from source text =="
python3 - <<'PY'
from pathlib import Path
import re

test = Path('tests/relay-dispatch/scripts/runtime-contract-noop.test.js').read_text()
facts = Path('skills/relay-dispatch/scripts/facts.js').read_text()

m = re.search(r'exactKeys\s*\(\s*([A-Za-z_$][\w$]*)\s*,\s*\[([^\]]+)\]\s*\)\s*:",\s*"([A-Za-z_$][\w$]*)"', test)
if not m:
    print('exactKeys call pattern not found, printing exactKeys occurrences')
    for i,line in enumerate(test.splitlines(),1):
        if 'exactKeys' in line:
            print(f'line {i}: {line.strip()}')
else:
    obj_name = m.group(1)
    keys = sorted(k.strip().strip('"\'') for k in m.group(2).split(','))
    label = m.group(3) or '<no label>'
    print('exactKeys object:', obj_name)
    print('keys count:', len(keys))
    print('keys:', keys)

# Compare no-op fixture exported keys and facts.js final exports.
noop_export = next(re.finditer(r'\bmodule\.exports\s*=\s*\{(?P<body>(?:[^{}]|\{(?P>n)\}[^{}\n]*)*?)\}\s*;', facts), None)
export_expr = next(re.finditer(r'\bmodule\.exports\s*=\s*\{(?P<body>.{0,150}?)\}\s*;', test) or re.finditer(r'\bmodule\.exports\s*=\s*\{(.*?);$', test))
# Actually, final line should be at end; get last object assignment.
last_obj = re.search(r'\bmodule\.exports\s*=\s*\{([^}]*)\}\s*;', facts, flags=re.S)
if last_obj:
    keys_from_export = sorted(re.findall(r'\b([A-Za-z_$][\w$]*)\s*:', last_obj.group(1)))
    print('facts.js module.exports count:', len(keys_from_export))
    print('facts.js module.exports keys:', keys_from_export)
    print('no_op_fixture_keys_differ_from_exports:', keys_from_export != sorted(re.findall(r'\b([A-Za-z_$][\w$]*)\s*:', Path('tests/relay-dispatch/fixtures/runtime-contract-noop.js').read_text())))
PY

Repository: sungjunlee/dev-relay

Length of output: 3394


no-op 픽스처와 런타임 계약 export 집합을 단말에서 맞춤으로 검증하세요.

runtime-contract-noop.js는 현재 RELAY_RUNTIME_PATH로 주입되고, runtime-contract.test.js에는 runtime export 키의 정확한 일치 검증이 없습니다. 팩트가 추가되거나 이름이 바뀌면 no-op 목록이 낡아도 contract test는 해당 함수가 없는 경우 createRunRecord is not a function만 보고 통과할 수 있습니다. productionRuntime과 custom runtime export 집합의 동기화를 exactKeys(...) 또는 동등한 검증에서 강제하세요.

🤖 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 `@tests/relay-dispatch/fixtures/runtime-contract-noop.js` around lines 3 - 18,
Update runtime-contract.test.js to compare the exact export keys of
productionRuntime and the injected runtime-contract-noop fixture, using
exactKeys(...) or equivalent. Ensure the test fails when either runtime adds,
removes, or renames an export, including keeping the no-op fixture synchronized
with the runtime contract.
tests/relay-dispatch/fixtures/write-containment-executor.js (1)

23-23: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

TMPDIR이 없으면 temp 프로브가 거짓 denied를 기록합니다.

23행은 process.env.TMPDIR || null을 사용합니다. Linux에서는 TMPDIR이 보통 설정되지 않습니다. 그러면 37행의 path.join(null, ...)TypeError를 던집니다. catch는 error.code를 읽지만 TypeError에는 code가 없으므로 proof.tempdenied:unknown이 됩니다.

결과적으로 샌드박스가 temp 쓰기를 실제로 차단하지 않아도 프로브가 차단된 것처럼 보입니다. containment 증거가 거짓 음성이 됩니다. os.tmpdir()을 fallback으로 사용하십시오.

🐛 제안 수정
 const fs = require("fs");
 const net = require("net");
+const os = require("os");
 const path = require("path");
 const { spawnSync } = require("child_process");
-const proof = { tempdir: process.env.TMPDIR || null };
+const proof = { tempdir: process.env.TMPDIR || os.tmpdir() };

Also applies to: 36-41

🤖 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 `@tests/relay-dispatch/fixtures/write-containment-executor.js` at line 23,
Update the temp-directory initialization in the proof setup to use os.tmpdir()
when process.env.TMPDIR is unset, ensuring path.join receives a valid directory
and the temp-write probe reports the actual containment result. Import or reuse
the existing os module in the fixture.
tests/relay-dispatch/scripts/facts.test.js (1)

349-352: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# capability 구조와 lock 식별 방식 확인
set -uo pipefail

echo "== capability 생성 및 등록"
rg -n -C 6 'issuedLocks|lockStates|Object\.freeze\(\{ lock_id' skills/relay-dispatch/scripts/host.js

echo "== requireLockHeld / assertRunLockHeld / stateFor 구현"
ast-grep run --pattern 'function $NAME($$$) { $$$ }' --lang javascript skills/relay-dispatch/scripts/host.js \
  | rg -n -A 12 'function (stateFor|assertRunLockHeld)\b' || true
rg -n -C 10 'function requireLockHeld' skills/relay-dispatch/scripts/facts.js

echo "== capability 에 owner 속성이 있는지"
rg -n -C 3 '\bowner\b' skills/relay-dispatch/scripts/host.js | rg -n 'capability|return' || true

Repository: sungjunlee/dev-relay

Length of output: 14747


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== facts.test.js lock tests around reported lines and capability helpers/setup"
sed -n '300,375p' tests/relay-dispatch/scripts/facts.test.js | cat -n

echo
echo "== lockContext usage and capability mocking in tests"
rg -n -C 5 'lockContext|acquireRunLock|lock_id|run_dir|owner:.*token|token' tests/relay-dispatch/scripts/facts.test.js

echo
echo "== facts.js appendFact / requireLockHeld implementation"
rg -n -C 8 'function appendFact|function releaseFact|requireLockHeld' tests/relay-dispatch/scripts/facts.js

Repository: sungjunlee/dev-relay

Length of output: 13083


🏁 Script executed:

#!/bin/bash
set -uo pipefail

echo "== fact lock functions in host.js"
sed -n '645,665p' skills/relay-dispatch/scripts/host.js | cat -n

echo
echo "== stateFor in facts.js with full path"
sed -n '80,110p' skills/relay-dispatch/scripts/facts.js | cat -n
sed -n '375,392p' skills/relay-dispatch/scripts/facts.js | cat -n

echo
echo "== host capability shape behavioral probe"
node - <<'JS'
const capability = Object.freeze({
  lock_id: "l1",
  attempt_id: "a1",
  operation: "dispatch",
  run_dir: "/tmp/run",
  host_kind: "local_supervisor",
  host_handle: "dispatch:42:abc"
});
const original = { ...capability, owner: { ...capability.owner, token: "forged" } };
console.log("capability has owner:", Object.prototype.hasOwnProperty.call(capability, "owner"), "owner value:", capability.owner);
console.log("original lock_id:", original.lock_id, "run_dir:", original.run_dir, "owner:", original.owner);
console.log("same object:", original === capability);
const issuedLocks = new WeakSet();
issuedLocks.add(capability);
console.log("WeakSet.has(original):", issuedLocks.has(original));
JS

Repository: sungjunlee/dev-relay

Length of output: 3999


스프레드 복제 capability 테스트를 교체합니다.

350행의 { ...lock, owner: { ...lock.owner, token: "forged" } }acquireFactLock이 반환한 frozen capability와 다른 객체 참조가 됩니다. stateFor()는 registered object 동일성으로 lock을 확인하므로 이 단언은 위조 token 검증을 하지 않고 복제된 lock context 객체를 거절하는 동작만 검증합니다. 테스트 이름의 “forged capabilities”를 실제 검증하려면, 발급된 capability를 공유한 상태 중 run_dir을 다른 실행의 값으로 바꾼 사례를 추가하십시오.

🤖 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 `@tests/relay-dispatch/scripts/facts.test.js` around lines 349 - 352, Replace
the forged-capability assertion around appendFact with a test that reuses the
capability returned by acquireFactLock while substituting run_dir with the value
from another execution. Keep the shared capability identity intact and assert
that appendFact rejects this mismatched execution context, so the “forged
capabilities” test validates run_dir checking rather than rejection of a cloned
lock object.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85cba6fed3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +178 to +183
const member = (name) => new RegExp(`\\b(?:test|it|describe)\\s*(?:\\.\\s*${name}|\\[\\s*(["'])${name}\\1\\s*\\])\\s*\\(`);
const option = (name) => new RegExp(`(?:\\{|,)\\s*(?:${name}\\s*:|\\[\\s*(["'])${name}\\1\\s*\\]\\s*:|${name}\\s*(?=[,}]))`);
if (member("only").test(executable)) violations.push(`${relative}: only registration is forbidden`);
if (member("todo").test(executable)) violations.push(`${relative}: todo is forbidden`);
if (member("skip").test(executable)) violations.push(`${relative}: skip registration is forbidden`);
if (/\b(?:test|it|describe)\s*\[/.test(maskedExecutable)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject optional-chained test directives

An optional chain bypasses all of these patterns: test?.skip("hidden", fn) does not match the member regex, computed-registration check, or direct-call parser, but Node accepts it and reports the test as skipped. Because this registration is never added to skips, the exact allowlist still matches and CI passes while an ordinary Relay test disappears; reject optional chaining on test registrations or parse these calls structurally.

Useful? React with 👍 / 👎.

@sungjunlee
sungjunlee merged commit abee561 into main Aug 9, 2026
10 checks passed
@sungjunlee
sungjunlee deleted the codex/1196-simplify-test-accounting branch August 9, 2026 15:20
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.

Simplify Relay test accounting and narrow internal testing seams

1 participant