Skip to content

feat(orchestrator): forward cost-attribution to contextual-orchestrator - #482

Closed
seonghobae wants to merge 1 commit into
developfrom
feat/orchestrator-cost-attribution
Closed

feat(orchestrator): forward cost-attribution to contextual-orchestrator#482
seonghobae wants to merge 1 commit into
developfrom
feat/orchestrator-cost-attribution

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Superseded by the hardened orchestrator integration order: #458 first, then issue #494.

Fresh reconciliation against the current ScopeWeave and contextual-orchestrator contracts found that this PR's unique value is still valid — forwarding service=scopeweave and the authenticated organization as the account cost-attribution dimension — but this branch is based on pre-hardening develop@74a5e99d... and its server/orchestrator.mjs diverges from the current production-hardening owner #458. Merging this branch would reintroduce the older unbounded/less strict transport instead of preserving #458's authenticated HTTPS-only endpoint validation, message/response bounds, timeout, fail-closed provider parsing, sanitized errors, and development-only deterministic adapter.

The unique semantic behavior and verification contract are preserved in #494 using fresh read-only contextual-orchestrator evidence (ALLOWED_CHAT_KEYS includes attribution; cost dimensions include account/service/upstream_api/provider/model_name/team/group/company). #494 explicitly requires rebuilding only the attribution delta after #458 integrates and forbids copying this PR's pre-hardening orchestrator substrate.

Closing this stale implementation removes an unsafe divergent PR without discarding its buyer-visible cost-observability requirement.

scopeweave's AI briefing (POST /api/projects/:id/ai/brief) has always
called contextual-orchestrator with only {model, messages} -- the client's
own comment explained this as caution about the orchestrator's strict
unknown-field rejection. Checked the actual contract
(ALLOWED_CHAT_KEYS/ATTRIBUTION_DIMENSIONS in
contextual-orchestrator/server.py + cost_ledger.py): attribution is an
explicitly allowed field (account/service/upstream_api/model_name/team/
group/company, plus a provider alias), so every scopeweave call has been
landing in the orchestrator's cost ledger as unattributed spend for no
real reason -- a real, buyer-visible gap for a repo that positions itself
as an org-wide LLM cost-review hub.

chat(messages, attribution) now accepts an optional second argument,
sanitizes it against the real allowed-dimension set (drops unknown keys
and null/empty values, coerces survivors to strings, omits the field
entirely rather than sending an empty object when nothing valid remains),
and forwards it. The ai/brief call site now sends
{ service: 'scopeweave', account: p.org_id }.

Fully backward compatible: chat(messages) with no second argument sends
exactly the same body as before (verified in the new test).

New tests/unit/orchestrator.test.mjs (registered in package.json's
test:unit script) exercises the real non-mock path by spawning a fresh
process with ORCHESTRATOR_URL set and a stubbed global fetch, matching
the existing tests/api/auth-secret.test.mjs pattern for env-var-gated
module state. Covers: known dimensions forwarded verbatim, unknown/empty
values dropped, attribution omitted when nothing survives sanitization,
and the no-argument case being byte-identical to prior behavior.

Verified: npm run test:unit (17/17 passed) and npm run test:api (3/3
passed, including the ai/brief mock-path smoke test) both green.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

AI 브리핑 호출에 serviceaccount attribution을 추가했습니다. 오케스트레이터는 허용된 값만 정제하여 요청에 포함합니다. 관련 단위 테스트와 실행 스크립트를 추가했습니다.

Changes

Attribution 전달

Layer / File(s) Summary
Attribution 정제 및 요청 구성
server/orchestrator.mjs
chat이 선택적 attribution을 받고, 허용된 serviceaccount 값만 문자열로 변환하여 요청 본문에 포함합니다. 유효한 값이 없으면 필드를 생략합니다.
호출 연결 및 검증
server/app.mjs, tests/unit/orchestrator.test.mjs, package.json, CHANGELOG.md
AI 브리핑 호출에 service: 'scopeweave'account: p.org_id를 전달합니다. 단위 테스트와 test:unit 스크립트가 attribution 전달 및 정제 동작을 검증합니다. 변경 내용을 changelog에 기록합니다.

Estimated code review effort: 2 (Simple) | ~10 minutes

Mergeability Score: ⚪ Minimal · up to c40e7

The change forwards sanitized cost-attribution metadata while preserving existing no-argument behavior; no actionable merge-blocking risk remains, and the PR is merge-ready after normal checks.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 contextual-orchestrator에 비용 귀속 정보를 전달하는 변경 사항을 정확하고 간결하게 설명합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/orchestrator-cost-attribution

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/unit/orchestrator.test.mjs (1)

30-36: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

허용된 값의 문자열 변환을 직접 검증하세요.

현재 테스트는 모든 유지 값을 문자열로 전달합니다. 따라서 String(value) 변환이 제거되어도 테스트가 통과합니다. account: 123과 같은 값을 전달하고 account: '123'을 기대값으로 검증하세요.

수정 예시
-  const { url, body } = chatRequestBody({ service: 'scopeweave', account: 'org-123' });
+  const { url, body } = chatRequestBody({ service: 'scopeweave', account: 123 });
   assert.equal(url, 'http://orchestrator.test/v1/chat/completions');
   assert.equal(body.model, 'contextual-orchestrator');
-  assert.deepEqual(body.attribution, { service: 'scopeweave', account: 'org-123' });
+  assert.deepEqual(body.attribution, { service: 'scopeweave', account: '123' });
🤖 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/unit/orchestrator.test.mjs` around lines 30 - 36, Update the
known-dimensions test around chatRequestBody to pass a non-string account value
such as 123, while continuing to assert the other request fields, and expect
attribution.account to be the string "123" so the String(value) coercion is
directly verified.
🤖 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.

Nitpick comments:
In `@tests/unit/orchestrator.test.mjs`:
- Around line 30-36: Update the known-dimensions test around chatRequestBody to
pass a non-string account value such as 123, while continuing to assert the
other request fields, and expect attribution.account to be the string "123" so
the String(value) coercion is directly verified.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 095d8a72-f49e-4a1d-a5e5-a6e18938d8c4

📥 Commits

Reviewing files that changed from the base of the PR and between 74a5e99 and c40e773.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • package.json
  • server/app.mjs
  • server/orchestrator.mjs
  • tests/unit/orchestrator.test.mjs

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head c40e77336f59b093701b48e57a4f0341ecf2dff7.

  • Head SHA: c40e77336f59b093701b48e57a4f0341ecf2dff7

  • Workflow run: 31701453934

  • Workflow attempt: 1

Coverage evidence

Coverage Decision

  • Result: FAIL
  • Test evidence: not proven passing
  • Docstring evidence: not proven passing when configured
  • Failure count: 1

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (4 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (4 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Test: orchestrator.test.mjs"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: orchestrator.test.mjs"]
  R2 --> V2["targeted test run"]
Loading

@opencode-agent

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: c40e77336f59b093701b48e57a4f0341ecf2dff7
  • Workflow run: 31701453934
  • Workflow attempt: 1
  • Gate result: REQUEST_CHANGES (approval step)

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head c40e77336f59b093701b48e57a4f0341ecf2dff7.

  • Head SHA: c40e77336f59b093701b48e57a4f0341ecf2dff7

  • Workflow run: 31701453934

  • Workflow attempt: 1

Coverage evidence

Coverage Decision

  • Result: FAIL
  • Test evidence: not proven passing
  • Docstring evidence: not proven passing when configured
  • Failure count: 1

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (4 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (4 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Test: orchestrator.test.mjs"]
  S2 --> I2["regression suite"]
  I2 --> R2["Review risk: Test: orchestrator.test.mjs"]
  R2 --> V2["targeted test run"]
Loading

@seonghobae
seonghobae marked this pull request as draft August 14, 2026 09:11

Copy link
Copy Markdown
Contributor Author

@opencode-agent @cwl-noema-review

Repair this as a draft on the live protected develop base. Preserve the buyer-visible contextual-orchestrator cost-attribution path, but rebuild the branch so inherited Hono CVEs and stale-base test contracts disappear rather than being suppressed. Add the outstanding coercion regression by passing a numeric account such as 123 and asserting the forwarded value is '123'; register the test in the current test:unit and coverage evidence without dropping any newer suites. Require exact-head Server Tests, coverage/docstring evidence, Security Scan, OSV, Semgrep and formal review before marking Ready. Do not mutate protected settings, waive Trivy findings, or merge from this draft.

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.

1 participant