feat: enforce purpose-limited PII event protection - #803
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAES-256-GCM 기반 PII 필드 보호를 추가했다. 감사 및 분석 이벤트는 지정 필드를 암호화해 저장한다. 관리자 감사 재생만 복호화한다. 관리자 경로는 scope별 purpose를 검증하고 인가 결정을 기록한다. 이벤트 스트림은 비동기로 저장하며 스트림별 보존 한도를 적용한다. Changes목적 제한형 PII 보호
Estimated code review effort: 4 (Complex) | ~60 minutes Merge Risk: 🟠 High · up to This change encrypts declared PII and alters event persistence behavior, but the current implementation can drop audit events, hang after save failures, and accept or expose improperly protected data. These availability, integrity, and confidentiality risks must be fixed or explicitly accepted before merging. Sequence Diagram(s)sequenceDiagram
participant AdminClient
participant SecurityConfig
participant TaskOrchestrator
participant PiiFieldEncryptor
participant SQLite
AdminClient->>SecurityConfig: scope 및 purpose 전달
SecurityConfig->>SecurityConfig: purpose 검증
SecurityConfig->>TaskOrchestrator: role 및 purpose 반환
AdminClient->>TaskOrchestrator: PII 이벤트 기록
TaskOrchestrator->>PiiFieldEncryptor: 지정 필드 암호화
PiiFieldEncryptor-->>TaskOrchestrator: 암호화 봉투 반환
TaskOrchestrator->>SQLite: 이벤트 스트림 비동기 저장
AdminClient->>TaskOrchestrator: audit_replay 요청
TaskOrchestrator->>PiiFieldEncryptor: 감사 이벤트 복호화
PiiFieldEncryptor-->>AdminClient: 복호화된 이벤트 또는 unavailable 반환
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
|
Reverification after review fixes (current HEAD |
|
Exact-head remediation record: base e226e11, head 33f312c. The source security finding is fixed by bounding durable audit retention to 256 rows; CI runtime locks now include cryptography and greenlet with hashes. Local evidence on this exact tree: 1448 passed in 527.54s, 38 focused persistence/security tests passed, hash-locked install plus editable install succeeded, actionlint/compileall/diff-check passed. Devin and CodeRabbit dispositions are recorded; all review threads are resolved. Hosted checks are rerunning and currently pending, so this remains a normal protected merge candidate, not a guarded-force candidate. |
|
Exact-head remediation follow-up (HEAD
Exact-head local evidence after this remediation:
Hosted evidence for |
|
Exact-head remediation follow-up (HEAD , parent ).\n\n- Devin : fixed at the root. Authorization decisions now use a separate bounded in-memory stream and durable SQLite kind, so denial or replay-auth churn cannot evict substantive PII audit events. Both streams retain 256 newest records and reload independently.\n- Devin : fixed. Authorized replay now degrades only an undecryptable/malformed row to its ciphertext envelope plus the non-sensitive marker ; other audit rows and the admin payload remain available. No exception/key material is returned.\n- Devin : disposition unchanged. Caching is by key name intentionally; ADR 0024 represents rotation by introducing a new key name and retaining old names until expiry/re-encryption.\n- Devin : canonical docs now describe the stdlib HTTP/core plus the selected runtime dependency; the existing hash-locked requirements and CI install paths are retained.\n- Devin : raw workflow/access/evaluation traces and remain surfaces by design. The stream-separation fix removes their authorization-decision noise from the substantive PII audit stream.\n- Devin : fail-closed behavior remains only for sensitive authorization auditing; ordinary operator-read routes do not write a successful replay decision.\n- Devin and : no source change warranted; analytics envelopes are intentionally write-only, and the current GET path authorizes before consuming the per-request role/purpose.\n\nExact-head local evidence after this remediation:\n- ........................................................................ [ 4%] |
|
Exact-head follow-up for
Decision remains |
|
Exact-head update: pushed |
Exact-head verification update
Review disposition:
Live gate remains protected and incomplete: open, non-Draft, mechanically mergeable but blocked; current head has 7 skipped and 15 queued check-runs, with no formal approval. This is normal WAIT_AND_REMEDIATE state, not D1-D5 deadlock. No merge or bypass was performed. |
|
Review repairs pushed at exact head 35692d5. The analytics stream and durable analytics retention now both use the 256-event contract. PII key parsing now rejects unprefixed raw 32-byte strings, preserves explicit base64:/hex: generated-key formats, and supports passphrase: through stdlib scrypt with key-name domain separation; tests cover deterministic derivation and rejection. Updated ADR 0024 and the research register with APA-style RFC 7914 evidence. Verification: full pytest 1455 passed; compileall and diff checks passed. Hosted checks are rerunning; no merge attempted without independent approval. |
…ryption # Conflicts: # contextual_orchestrator/orchestrator.py # contextual_orchestrator/server.py # fuzz/requirements-atheris.in # fuzz/requirements-atheris.txt # pyproject.toml
Rebase onto #769's runtime boundary: hypothesis moves out of runtime deps (stays in the test extra), cryptography joins as the only production dependency for PII field encryption. Regenerated requirements.lock via the canonical pip-compile command (hypothesis-free, hash-locked) and both fuzz lockfiles; hand-preserved the CPython 3.12 Atheris marker per its contract test.
Resolves fresh lockfile conflicts: uv.lock and requirements.lock regenerated (uv lock / pip-compile), fuzz/requirements-property.txt regenerated via uv pip compile -- none hand-merged.
| def _drain_stream_queue(self) -> None: | ||
| while True: | ||
| with self._stream_condition: | ||
| while not self._stream_closing and not any(self._stream_events.values()): | ||
| self._stream_condition.wait() | ||
| event = self._next_stream_event() | ||
| if event is None: | ||
| return | ||
| kind, key, payload = event | ||
| self._stream_writing = True | ||
| try: | ||
| self._save_sync(kind, key, payload) | ||
| except Exception: # noqa: BLE001 - a best-effort stream write must not stop later persistence. | ||
| pass | ||
| finally: | ||
| with self._stream_condition: | ||
| self._stream_writing = False | ||
| self._stream_condition.notify_all() | ||
|
|
||
| def _next_stream_event(self) -> tuple[str, str | None, dict[str, Any]] | None: | ||
| """Return one pending event fairly; caller holds ``_stream_condition``.""" | ||
| kinds = tuple(self._STREAM_LIMITS) | ||
| for offset in range(len(kinds)): | ||
| index = (self._next_stream_index + offset) % len(kinds) | ||
| kind = kinds[index] | ||
| if self._stream_events[kind]: | ||
| self._next_stream_index = (index + 1) % len(kinds) | ||
| key, payload = self._stream_events[kind].popleft() | ||
| return kind, key, payload | ||
| return None | ||
|
|
||
| def _flush_streams(self) -> None: | ||
| with self._stream_condition: | ||
| while self._stream_writing or any(self._stream_events.values()): | ||
| self._stream_condition.wait() |
There was a problem hiding this comment.
📝 Info: Async stream worker has no lost-wakeup/deadlock
The _StateStore background worker uses a single notify() in save(), but the worker is the sole waiter on an empty queue and re-checks the queue each loop; _flush_streams only waits while work is pending or in flight. _stream_condition is released before _save_sync takes _lock, so load()/close() cannot deadlock. Verified no hang or dropped events.
Was this helpful? React with 👍 or 👎 to provide feedback.
| def save(self, kind: str, key: str | None, payload: dict[str, Any], *, durable: bool = False) -> None: | ||
| if kind in self._STREAM_LIMITS and not durable: | ||
| with self._stream_condition: | ||
| if self._stream_closing: | ||
| raise RuntimeError("state store is closed") | ||
| self._stream_events[kind].append((key, payload)) | ||
| self._stream_condition.notify() | ||
| return | ||
| self._save_sync(kind, key, payload) |
There was a problem hiding this comment.
📝 Info: Analytics and denials become best-effort async
_append_audit_event defaults durable=True, so audit writes stay synchronous (now DB-capped at 256). Only record_analytics_event and authorization denials go through the async best-effort queue, which can silently drop on queue overflow or a failed worker write. Intended per the ADR, but analytics/denials are no longer guaranteed durable.
Was this helpful? React with 👍 or 👎 to provide feedback.
| def record_analytics_event( | ||
| self, | ||
| event_name: str, | ||
| detail: dict[str, Any], | ||
| *, | ||
| pii_fields: Iterable[str] = (), | ||
| ) -> None: | ||
| """Record a compact in-memory analytics event without prompt or output text.""" | ||
| require_object_name(event_name, "analytics.event_name") | ||
| event = { | ||
| "event_time": int(time.time()), | ||
| "event_name": event_name, | ||
| "event_detail": redact_value(detail), | ||
| "event_detail": redact_value(self._protected_event_detail(detail, pii_fields)), | ||
| } | ||
| self._analytics_events.append(event) | ||
| if self._store is not None: |
There was a problem hiding this comment.
📝 Info: Analytics-encrypted PII is never decryptable
record_analytics_event encrypts declared pii_fields, but only list_recent_audit_events has a decrypt/replay path (admin + audit_replay). Analytics PII envelopes have no recovery surface, so PII placed there is effectively write-only. Appears deliberate; noted for awareness.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Merge-gate evidence (2026-08-24): Deep diff review + fixes applied; all required checks green on current head except strix (org-wide NVIDIA NIM quota exhaustion — external provider-capacity blocker; serialization fix in ContextualWisdomLab/.github#1297). Full local suite green on this head. |
# Conflicts: # .github/workflows/fuzz.yml # contextual_orchestrator/orchestrator.py # docs/fuzzing.md # fuzz/targets.py
| if not isinstance(pii_key_name, str) or not pii_key_name: | ||
| raise ValueError("pii_key_name must be a non-empty string") |
There was a problem hiding this comment.
🟡 State store thread leaks when PII key name is rejected
The durable state store starts its background thread and opens its sqlite connection (_StateStore(state_db)) before pii_key_name is validated. An invalid name then raises ValueError, orphaning that thread and connection for the life of the process because close() never runs.
Prompt for agents
In TaskOrchestrator.__init__ the pii_key_name validation (the isinstance/non-empty check that raises ValueError) is placed after self._store = _StateStore(state_db) is created. _StateStore.__init__ starts a daemon worker thread and opens a sqlite connection, and only close() tears those down. When pii_key_name is invalid the ValueError propagates out of __init__ before the caller ever gets a reference to close(), leaking the thread and connection. Move the pii_key_name validation (and assignment of self._pii_key_name / self._pii_encryptors) so it runs before the _StateStore is constructed, so an invalid argument fails before any resource is allocated.
Was this helpful? React with 👍 or 👎 to provide feedback.
| 5. **PII key boundary** — unprefixed encryption-key text must be rejected; | ||
| accepted key material must declare `base64:`, `hex:`, or `passphrase:`. |
There was a problem hiding this comment.
🟡 Duplicate list number in fuzzing doc
The new PII key-boundary target is numbered 5., the same as the reasoning-effort profile entry directly below it. The target list now has two item 5s and no item 6.
Prompt for agents
The numbered target list in docs/fuzzing.md now contains two entries labeled 5: the newly inserted PII key boundary item and the pre-existing reasoning-effort profile item that follows it. Renumber so the PII key boundary is 5 and the reasoning-effort profile is 6 (matching the 7/8 renumbering already applied in fuzz/targets.py).
Was this helpful? React with 👍 or 👎 to provide feedback.
| if role != "admin" or purpose != "audit_replay": | ||
| return selected | ||
| restored: list[dict[str, Any]] = [] | ||
| encryptors: dict[str, Any] = {} | ||
| for event in selected: | ||
| detail = event.get("event_detail") | ||
| if not is_encrypted_detail(detail): | ||
| restored.append(event) | ||
| continue | ||
| restored_event = dict(event) | ||
| try: | ||
| metadata = detail.get(ENCRYPTED_FIELDS_KEY) | ||
| key_name = metadata.get("key_name") if isinstance(metadata, dict) else self._pii_key_name | ||
| if not isinstance(key_name, str) or not key_name: | ||
| raise PiiProtectionError("encrypted field metadata has no valid key name") | ||
| encryptor = encryptors.get(key_name) | ||
| if encryptor is None: | ||
| encryptor = load_pii_encryptor(key_name) | ||
| encryptors[key_name] = encryptor | ||
| restored_event["event_detail"] = encryptor.decrypt_fields(detail) | ||
| except PiiProtectionError: | ||
| restored_event["event_detail"] = { | ||
| **detail, | ||
| "__pii_protection_error__": "unavailable", | ||
| } | ||
| restored.append(restored_event) | ||
| return restored |
There was a problem hiding this comment.
📝 Info: PII plaintext exposure limited to admin audit_replay
Decryption in list_recent_audit_events is gated on role admin and purpose audit_replay, and the purpose is chosen by the route via _admin_purpose(path) rather than request data, so callers cannot escalate. Only /admin/state maps to audit_replay; the other admin_state() caller passes no role/purpose and sees ciphertext. Consequence worth noting: every /admin/state GET returns plaintext PII to any admin-token holder and forces a synchronous durable authorization write that returns 503 if it fails.
Was this helpful? React with 👍 or 👎 to provide feedback.
# Conflicts: # contextual_orchestrator/server.py
|
Merge-gate evidence (2026-08-25): Integrated with the merged opaque-session auth design (#788) — purpose resolution now composes with session/bearer validation, denials and audit_replay access are audited, and state-changing admin routes keep the same-origin check. Full local suite green on this head. All required hosted checks green except strix (org-wide NVIDIA NIM quota exhaustion — external provider-capacity blocker; serialization fix in ContextualWisdomLab/.github#1297). |
#803 enforcement landed; the admin console row now describes the active control (purpose-authorized roles, field encryption, audited release) instead of the pre-implementation Proposed label.
…omLab#762) * docs: design purpose-limited PII protection * docs(security): close PII ADR review gaps * fix: align admin PII policy with non-masking ADR * fix: label proposed PII controls and canonicalize ADR * test: assert the landed purpose-limited PII control row ContextualWisdomLab#803 enforcement landed; the admin console row now describes the active control (purpose-authorized roles, field encryption, audited release) instead of the pre-implementation Proposed label. --------- Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
* docs: track embedding integration PR * docs: record reasoning profile regression proof * docs: refresh latest gateway and judge evidence * docs: record review gateway full-suite proof * docs: refresh exact product gap evidence * docs: refresh release gap baseline * docs: resolve baseline review findings * docs: bind baseline to current PR head * docs: refresh latest gap baseline heads * docs: refresh current PR gap baseline * docs: bind gap baseline to current head * docs: avoid stale self head evidence * docs: record central gateway dependency * docs: reserve unique product baseline ADR number * docs: refresh embedding ADR evidence * docs: record merged trace authorization stack * docs: record completed stacked merges * docs: bind baseline to current provider stack * docs: bind multimodal stack to current head * docs: refresh product gap baseline evidence * docs: record stacked multimodal merge * docs: record Strix rerun evidence * docs: add parent regression evidence * docs: refresh central gateway evidence * docs: refresh current PR and release evidence * docs: record current pool-gate and release evidence * docs: clarify central gateway migration boundary * docs: bind baseline to latest release evidence * docs: bind baseline to latest exact heads * docs: record atheris PR promotion * docs: refresh exact release evidence * docs: bind baseline to current release heads * docs: record independent current-head proof * docs: keep observed PR evidence exact * docs: record live protected approval requirements * docs: refresh live product gap heads * docs: restore PR inventory table rendering * docs: record database naming gap * docs: bind naming proof to current head * docs: refresh product gap baseline evidence * docs: add agent pool gap evidence * docs: refresh stacked PR baseline heads * docs: record reconciled passthrough stack * docs: refresh central gateway prerequisite * docs: record partial endpoint race predecessor * docs: record ledger and hourly loop PRs * docs: refresh hourly gateway prerequisite * docs: refresh cost ledger evidence head * docs: refresh central gateway prerequisite head * docs: record cost ledger full verification * docs: record exact cost ledger verification * docs: track closed hourly caller and active central PR * docs: refresh exact stacked PR evidence * docs: refresh agent-pool stack evidence * docs: refresh cost-ledger stack evidence * docs: record naming and ledger repair heads * docs: record stable current stacked heads * docs: correct current cost-ledger stack evidence * docs: record exact append rollback suite * docs: refresh current PR heads and proof boundaries * docs: refresh current PR evidence snapshot * docs: record duplicate scheduler closure * docs: record canonical central scheduler and stack proof * docs: refresh model discovery coverage evidence * docs: record chat capability security repair * docs: refresh currency ranking evidence * docs: record PR 765 security repair evidence * docs: record PR 765 SSRF repair * docs: record stacked CLI and lint PRs * docs: track current CLI stack * docs: record exact PR 765 suite evidence * docs: record responses review disposition * docs: pin external scheduler evidence * docs: refresh provider discovery head * docs: refresh gateway stack evidence * docs: refresh gateway route evidence * docs: refresh baseline for current PR queue * docs: align baseline snapshot timestamp * docs: record current CLI test repair * docs: refresh baseline for current PR heads * docs: refresh PR 805 exact-head baseline * docs: refresh product technical gap snapshot * docs: refresh PR 803 exact-head evidence * docs: record stacked release authority verification * docs: record current stacked PR 805 head * docs: refresh PR 805 exact-head evidence * docs: record PR 807 and 808 live gates * docs: refresh PR 807 verification baseline * docs: refresh PR 805 exact head * docs: record PR 809 verification baseline * docs: refresh PR 802 exact head * docs: refresh PR 771 and 805 states * docs: record current capability PR evidence * docs: invalidate stale tool-fallback evidence * docs: record latest live PR gate states * docs: record full PR 810 local verification * docs: record remote remediation PRs * docs: record exact PR 803 audit remediation * docs: record normal stack merges * docs: distinguish baseline and live recheck times * docs: record current PII retention verification * docs: record stale no-op stack item * docs: record exact current PR verification * docs: refresh exact PR gate evidence * docs: record queue dependency triage * docs: record bounded PR verification * docs: record protected auto merge state * docs: record central CodeQL follow-up * docs: refresh central exact-head control-plane evidence * docs: record central OSV repair successor * docs: classify superseded central hosted evidence * docs: refresh contextual live PR heads * docs: refresh scheduler current-head evidence * docs: record OSV reporter contract repair * docs: refresh contextual PR gate evidence * docs: classify current central audit gates * docs: refresh latest central check counts * docs: refresh central PR evidence * docs: record current OIDC caller evidence * docs: refresh consolidated central stack evidence * docs: refresh hosted gate counts * docs: refresh contextual hosted gate evidence * docs: refresh central live-head evidence * docs: record current central docstring verification * docs: record current OIDC caller verification * docs: refresh OTEL and sampling PR evidence * docs: refresh central stack and root evidence * docs: record current OIDC stack verification * docs: record restacked coverage PR * docs: refresh live PR deadlock evidence * docs: record cross-fork OSV repair * docs: record live governance and queue evidence * docs: record central queue refresh * docs: refresh contextual exact-head evidence * docs: refresh agent pool head evidence * docs: record branch coverage evidence * docs: refresh central restack evidence * docs: refresh sampling PR evidence * docs: refresh current OSV repair restack * docs: consolidate backlog convergence and close the denial-recording DoS gap All 29 open PRs are now independently verified clean (zero unresolved threads, mergeable, green checks), blocked solely on the shared external OpenCode App installation rate limit -- not a sampled subset as the prior per-PR churn implied. Replace that granular, fast-staling bookkeeping with one consolidated fact and strengthen the existing P0 delivery-gate row's evidence accordingly. Also record the authorization-denial persistence DoS found and fixed while triaging #803 (unauthenticated denials were forcing synchronous, lock-serialized disk commits shared with durable workflow_run/evaluation_run state) and update the P1 PII gap row: #803 is now code-complete, not just "open". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: record the admin-console UI-tooling deferral as ADR 0011 The standing org UI instructions require Figma/Storybook/ui-ux-pro-max/ Anti-Slop-UI for UI work, with the decision recorded in an ADR either way. That decision existed only as one sentence inside the living gap-baseline snapshot. Give it a standalone ADR: cites the existing Figma file (vsZMd8WAv42HDRgcZuNcWk), states why a Node/Storybook toolchain isn't warranted for one stdlib-only inline admin console today, and names three concrete, checkable conditions that would make adoption correct rather than optional. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: pin the canonical ADR directory in CLAUDE.md PR #818's branch independently created docs/adr/0122-... while every other ADR (0001-0011) lives in docs/planning/adrs/ -- no numeric collision, but no documented convention either, so the drift will keep happening. Pin docs/planning/adrs/ as canonical so future contributors converge without needing to discover it by grepping prior PRs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: correct the Atheris fuzzing Python-version note CLAUDE.md said "Python < 3.13"; pyproject.toml's actual fuzz extra marker is atheris==3.1.0; python_version >= '3.12' (the opposite bound), matching .github/workflows/fuzz.yml's comment that 3.1.0 covers both the 3.12 fuzz runner and the central 3.14 coverage-evidence image. Found while checking whether issue #95 (portable Atheris lock) is still open work. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: record issue #95's closure in the gap-baseline queue Follow-up to the CLAUDE.md Atheris-version fix: issue #95 is now closed (resolved on main by a single version-gated pin, not the originally-scoped two-way marker split), so drop its row from the open queue with a note on why, matching this document's existing convention for stale/closed items. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: document the missing model_discovery.py module in CLAUDE.md contextual_orchestrator/ has 13 real modules; CLAUDE.md's architecture overview only documented 12 -- model_discovery.py (auto-discovery across every KV-registered provider credential plus price-honest bootstrap selection) was entirely absent, a real onboarding gap for a module this central to the "auto model discovery" requirement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: link the two orphaned docs/*.md files from README's Design Artifacts docs/fuzzing.md and docs/product-technical-gap-baseline.md both exist and are referenced elsewhere (CLAUDE.md, this session's own gap-closing work) but neither was linked from README's Design Artifacts index -- found by diffing docs/*.md against README's linked set, same mechanical-check approach that found the model_discovery.py doc gap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: fix stale class names in architecture.md's implementation mapping The mapping named a class Agent and a class Orchestrator with route_once/ conduct methods; the actual code (verified directly) is ModelAgent and TaskOrchestrator.route_once/.conduct. A reader tracing this doc into the source would fail to find class Agent or class Orchestrator at all. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: document two undocumented API routes in README's Architecture section Diffed server.py's real /api/v1/*/latest routes against README's documented endpoint list: provider_readiness/latest and analytics_snapshots/latest both exist and work but were never listed alongside the other 26 already documented there. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: correct stale admin-merge authorization claim, close 3-day plan gap This track's "read this first" section claimed gh pr merge --admin is "real, working, authorized... not a bypass to ask permission for each time." Directly tested this session: it fails. Ruleset 18156473 has bypass_actors: [] today -- the exit-condition procedure this same section describes further down was apparently completed after iteration 76 (2026-08-20) without anyone coming back to correct the evergreen claim at the top. A future agent trusting this file could waste real effort on a bypass that no longer exists, or worse, believe it holds standing authorization it doesn't. Corrected the claim in place (kept for history, with the correction directly above it), fixed the stale iteration-10 pointer, and added a full dated Status entry closing the 3-day gap between this file and the session's actual work (5 PRs converged clean, a real DoS fix, issue #95 closed on verified evidence, and 6 doc-accuracy fixes across the repo) -- this file's own convention is to log every iteration, and it hadn't been. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: fix the same stale Agent/Orchestrator names in tech-stack.md Same bug as f5f9c2a's architecture.md fix, found in a second file: conductor/tech-stack.md's DDD layer mapping named Agent and Orchestrator, which don't exist in the source. Real names are ModelAgent and TaskOrchestrator. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: address baseline review gaps * docs(agents): remove stale KV known-deviation; align openai example with credential_key AGENTS.md still claimed ModelClient reads os.environ.get(agent.api_key_env). The runtime already resolves provider keys and server tokens from the KV registry via get_credential(). Update the guidance and the example agent pool to use the modern credential_key field. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: restore document-mismatch as a blocked-status cause on 4 commercial reports An 18-agent workflow audit (sweep + adversarial verify) found 9 real, independently-confirmed documentation/code discrepancies. Real code bug: commercial_procurement_readiness_report, commercial_contract_readiness_report, commercial_onboarding_readiness_report, and commercial_operations_readiness_report each omit "document mismatch" from their blocked-status rule string, while 10+ sibling report methods in the same file correctly include it. The blocking logic itself (blocked_count = ... + len(concrete_blockers)) already treats document mismatches as real blockers via commercial_release_candidate_report's release_gates -- only the buyer-facing rule-string explanation silently dropped the phrase on these four reports, misdescribing what actually blocks the status. Fixed all four rule strings and added a regression test per report (none existed before; this was unguarded). Doc-only fixes: docs/commercial_launch_readiness.md and docs/analytics_spec.md named a field, commercial_launch_external_input_count, that has never existed in the API -- the real field is launch_summary.external_input_group_count, already correctly locked by an existing runtime test. Updated both docs and the two doc-text-presence assertions in test_plugin_driven_artifacts.py that were locking the wrong string. docs/fuzzing.md had three separate stale claims (wrong Python version, a Targets list missing 2 of the actual 6 fuzzed surfaces, and a "running locally" list missing the 5th command) -- fixed all three, and fixed fuzz/targets.py's own docstring inconsistency (said "five surfaces" above a list of six) found in the same pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: log iteration 78 in the track plan (ultracode doc-audit workflow) Per iteration 77's own checklist: keep this file updated going forward, not just session-local memory. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: fix a third occurrence of the stale Agent/Orchestrator names conductor/workflow.md's DDD glossary had the same bug already fixed in architecture.md (f5f9c2a) and tech-stack.md (49fb1f8). Grepped the whole repo's *.md files for the pattern after this fix -- confirmed no remaining occurrences. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: close the remaining gap to 100% docstring coverage interrogate reported 95.8% against the org's 100% target (well above the enforced 80% CI gate) with exactly 11 missing docstrings -- small and bounded, unlike a full re-audit. Added one-line docstrings to all 11: 5 in cost_ledger.py (NoopUsageTelemetrySink.emit_usage, InMemoryUsageTelemetrySink.emit_usage/events, UsageTelemetryHealth.as_dict, NonBlockingLedgerStore.telemetry_health) and 6 in server.py (the Handler class itself, do_GET/do_PATCH/do_DELETE/do_POST/log_message). interrogate now reports 100.0%. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: log iteration 79 (docstring coverage close, peer coordination round) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: keep protected-merge evidence exact * docs: refresh exact protected-gate evidence * docs: include current local-test PR evidence * docs: refresh exact protected-gate evidence * docs: record capability-safe discovery evidence * docs: refresh model discovery evidence * docs: track LineageWeave CLI gate * docs: track pending shared Strix repair * docs: refresh shared Strix repair head * docs: track active Strix successor * docs: link Strix stack parent * docs: record shared Strix rollout block * docs: refresh central Strix recovery evidence * docs: clarify protected Strix bootstrap block * docs: record shared Strix fallback failure * docs: ground LineageWeave consumer gate * docs: assign unique admin UI ADR number * docs: record model-group feature gap evidence * docs(launch): align renamed analytics field * docs: log 2026-08-25 continuation (model groups, free discovery, hourly loop, queue recheck) * docs(baseline): remove transient model coupling * docs(baseline): refresh scheduler exact head * docs: refresh capability-group exact-head baseline * docs: record normalized group admin stack * docs: refresh capability routing exact-head evidence * docs: record modality discovery remediation evidence * docs: trace model group product specification * docs: record exact v0.2.0 candidate evidence * docs: record adjacent PR remediation heads * docs: record free orchestration and compose evidence * docs: refresh free orchestration exact head * docs: refresh reviewed orchestration evidence * docs: record structured free judge evidence * docs: refresh reasoning stream exact-head evidence * docs: record responses ledger gap * docs: refresh multimodal virtual model evidence * docs: bind multimodal evidence to current stack * docs: refresh free routing exact-head evidence * docs: record free routing stack merge * docs: refresh exact model routing delivery baseline * docs: align fuzz target inventory * docs: refresh exact-head protected queue evidence * docs: refresh model discovery gap evidence * docs: record group judge routing repair * docs: correct exact routing head identity * docs: refresh protected-main and free catalog evidence * docs: include model judge fuzz command * docs: refresh exact-head PR evidence * docs: record telemetry repair and PR decomposition * docs: refresh exact-head routing and CI gaps * docs: record current routing and telemetry heads * docs: record model-group API review repair * docs: record effective catalog KV repair * docs: record telemetry full-suite evidence * docs: record catalog sync operator guidance * docs: align gap baseline with current PRD * docs: record Bytez chat discovery repair * docs(adr): reserve admin console decision identifier * docs: reserve cross-PR ADR identifiers * docs: refresh exact-head product gap evidence * docs: sync scheduled-loop exact head * docs: separate contract and strategic value evidence * docs: record secured k6 exact-head evidence * docs: track orphaned performance recovery gap * docs: refresh exact-head delivery evidence * docs: record ledger review remediation * docs: refresh routed and web exact heads * docs: correct exact-head hashes * docs: record structured free-cost contract * docs: track constant-time budget recovery * docs: record hosted cache test repair * docs: refresh budget meter exact head * docs: track recovered passthrough failover slice * docs: record database PR cache-test repair * docs: sync passthrough review repair * docs: record async server hosted repair * docs: record fail-closed passthrough review * docs: record structured orchestration stack evidence * docs: record exact coverage repair evidence * docs: record structured control review repairs * docs: refresh exact-head routing evidence --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Summary
Verification
Scope note
Callers must pass pii_fields when an audit or analytics detail contains PII. This explicit declaration avoids unreliable PII detection and preserves usable content. Authorized admin replay decrypts protected fields; ordinary internal reads retain ciphertext. Key metadata supports retaining older KV keys during rotation.
This PR does not self-approve or bypass protected merge requirements. It awaits independent review and terminal GitHub Checks.
Summary by CodeRabbit
새 기능
문서
버그 수정