feat(schema): add mode to get_plan_audit_logs audit entries#3160
Merged
feat(schema): add mode to get_plan_audit_logs audit entries#3160
Conversation
Adds optional `mode` field ($ref governance-mode.json) to entries[] items in get-plan-audit-logs-response. Records the enforcement posture (enforce/advisory/audit) active at check time so auditors can distinguish silent-log from blocking governance decisions in the audit trail. https://claude.ai/code/session_0185Q8ottjpU8xDRzXQ5aJwx
Adds the new entries[].mode field to the response field table and shows it in the example execution check. Without this, the schema addition lands without doc parity — readers of the task page wouldn't know mode exists or what it means. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
d3327a7 to
3f01fb9
Compare
Merged
3 tasks
Merged
bokelley
added a commit
that referenced
this pull request
Apr 25, 2026
…per-check mode semantics (#3169) Two follow-ups from the #3160 pre-PR review: 1. governance-mode.json gains x-status: "experimental" — the enum is referenced exclusively from experimental schemas (check-governance-response, get-plan-audit-logs-response entries[]), so annotating it explicitly prevents the enum from looking stable while its consumers aren't. 2. The entries[].mode description is tightened to clarify it is a per-check value: each entry records the mode active for *that* check, not a rollup. Adds a forward-pointing note that a future governed_actions[].mode field would describe the action's current mode, which may differ from the most recent entry's mode if the plan has been re-synced since. Both schemas already carry x-status: experimental; this is description-only plus an enum-level annotation. Non-breaking. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
6 tasks
bokelley
added a commit
that referenced
this pull request
Apr 25, 2026
…rom anonymous knowledge tools Security fixes from expert review of PR #3175: 1. **Private working-group docs leaking via search_docs** server/src/db/working-group-db.ts: getIndexedDocumentsWithContent now filters wg.is_private = false. Without this, exposing search_docs to anonymous web-chat (the headline change in this PR) widens the surface from "anyone with an MCP client" to "anyone on the public web" for committee minutes, brand-confidential briefs, and draft policy entries indexed from private working groups. Authenticated WG members still access their content via the WG-specific pages — that path doesn't ride this index. 2. **Prompt-injection pipeline via user-bookmarked URLs** bookmark_resource (Slack-authenticated) queues arbitrary URLs that get fetched, summarized into addie_notes, and surfaced via search_resources/ get_recent_news. Anonymous Addie inherits that surface and the notes land in its prompt as "Addie's Take." Two-layer fix: - DB layer: searchCuratedResources / getRecentNews accept excludeUserSubmitted to drop source_type='web_search' (and 'community' for news) from results. - Handler layer: createKnowledgeToolHandlers gains an `anonymous` option; when true, passes excludeUserSubmitted through and strips addie_notes from formatted output. Both addie-chat.ts (web) and chat-tool.ts (MCP) pass anonymous: true on the global registration. Authenticated callers get the full handler via per-request override (claude-client.ts:594 "last wins" merge). Doc / prompt-engineering nits: 3. audit-trail.mdx: add entries[].mode and entries[].purchase_type rows to the field-tagging table (mode is the whole point of #3160 #3156); note that budget.utilization_pct is a one-step inverse problem (just as leaky as raw amounts); soften "on request" wording for drift_metrics to match the §103 "protocol does not define a regulator API" caveat. 4. constraints.md: tighten "Tool Unavailable Is Not 'No Result'" rule — drop fragile back-reference, replace upsell script with a behavioral shape (don't pitch; one line is enough), make the no-retry rule unambiguous ("Do not retry. One failure is the signal."). 5. SKILL.md: rename "Three invariants to lead with" to "Three invariants for audit and disclosure decisions" (the skill loads into orchestrator context, not a chat persona); demote effective_date to a doc-link note and promote plan_hash as the third load-bearing invariant for counterparty disclosure. 6. addie-chat.ts: anonymousTools log line now uses claudeClient.getRegisteredTools().length as source of truth instead of a hand-rolled sum that would silently drift. 7. gen-governance-audit-examples.ts: FIXME annotation referencing FRAMEWORK_MIGRATION.md so the in-flight server.server._requestHandlers migration sweep catches this script. Verified end-to-end: anonymous local Addie answering the original WG question still produces grounded retrieval (6 tool calls, 0 errors, cites the audit-trail doc) — the security scoping doesn't break the legitimate use case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
Apr 25, 2026
…nymous knowledge tools (#3175) * docs(governance): audit-trail internal-vs-shareable views + Addie anonymous knowledge tools Three related changes from a governance WG question on how to design audit logging: 1. New doc page docs/governance/campaign/audit-trail.mdx — explains the internal-vs-shareable view split for get_plan_audit_logs with a field-by-field tagging table and four worked examples generated by scripts/gen-governance-audit-examples.ts: clean buy, security-shaped denial (seller_compliance), coaching-shaped denial (Annex III prerequisite), and the enforce/advisory/audit mode comparison. All schema-tagged JSON blocks validate against the canonical schemas via tests/json-schema-validation. 2. Wire anonymous web-chat callers to receive search_docs, get_doc, search_repos, search_resources, and get_recent_news. Anonymous web chat previously had directory tools only; the system prompt told Addie to call search_docs and the tool wasn't registered, producing speculative answers instead of grounded ones. The MCP chat path already exposed the same set; this aligns the web path. Anonymous tool count: 7 → 13. 3. Constraints rule "Tool Unavailable Is Not 'No Result'" — distinguishes tool-returned-empty (say "I didn't find it") from tool-unavailable (say "I couldn't reach docs search; sign in for grounded answers"), and caps retries at one. Generalizes beyond search_docs to every tool. 4. skills/adcp-governance/SKILL.md gains the campaign-governance task surface (sync_plans, check_governance, report_plan_outcome, get_plan_audit_logs) and three operator-facing invariants: inline policies cannot relax registry policies, effective_date enables informational-before-enforcement, governance_context is the seller-visible correlation token while plan-level data is buyer-side. Filed and resolved upstream from this work: #3139, #3140, #3156 (→ #3160 merged), #3162 (→ #3163 merged), #3169 merged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: review feedback — block private WG leak and bookmark-injection from anonymous knowledge tools Security fixes from expert review of PR #3175: 1. **Private working-group docs leaking via search_docs** server/src/db/working-group-db.ts: getIndexedDocumentsWithContent now filters wg.is_private = false. Without this, exposing search_docs to anonymous web-chat (the headline change in this PR) widens the surface from "anyone with an MCP client" to "anyone on the public web" for committee minutes, brand-confidential briefs, and draft policy entries indexed from private working groups. Authenticated WG members still access their content via the WG-specific pages — that path doesn't ride this index. 2. **Prompt-injection pipeline via user-bookmarked URLs** bookmark_resource (Slack-authenticated) queues arbitrary URLs that get fetched, summarized into addie_notes, and surfaced via search_resources/ get_recent_news. Anonymous Addie inherits that surface and the notes land in its prompt as "Addie's Take." Two-layer fix: - DB layer: searchCuratedResources / getRecentNews accept excludeUserSubmitted to drop source_type='web_search' (and 'community' for news) from results. - Handler layer: createKnowledgeToolHandlers gains an `anonymous` option; when true, passes excludeUserSubmitted through and strips addie_notes from formatted output. Both addie-chat.ts (web) and chat-tool.ts (MCP) pass anonymous: true on the global registration. Authenticated callers get the full handler via per-request override (claude-client.ts:594 "last wins" merge). Doc / prompt-engineering nits: 3. audit-trail.mdx: add entries[].mode and entries[].purchase_type rows to the field-tagging table (mode is the whole point of #3160 #3156); note that budget.utilization_pct is a one-step inverse problem (just as leaky as raw amounts); soften "on request" wording for drift_metrics to match the §103 "protocol does not define a regulator API" caveat. 4. constraints.md: tighten "Tool Unavailable Is Not 'No Result'" rule — drop fragile back-reference, replace upsell script with a behavioral shape (don't pitch; one line is enough), make the no-retry rule unambiguous ("Do not retry. One failure is the signal."). 5. SKILL.md: rename "Three invariants to lead with" to "Three invariants for audit and disclosure decisions" (the skill loads into orchestrator context, not a chat persona); demote effective_date to a doc-link note and promote plan_hash as the third load-bearing invariant for counterparty disclosure. 6. addie-chat.ts: anonymousTools log line now uses claudeClient.getRegisteredTools().length as source of truth instead of a hand-rolled sum that would silently drift. 7. gen-governance-audit-examples.ts: FIXME annotation referencing FRAMEWORK_MIGRATION.md so the in-flight server.server._requestHandlers migration sweep catches this script. Verified end-to-end: anonymous local Addie answering the original WG question still produces grounded retrieval (6 tool calls, 0 errors, cites the audit-trail doc) — the security scoping doesn't break the legitimate use case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3156
Adds an optional
modefield ($ref: /schemas/enums/governance-mode.json) toentries[]items inget-plan-audit-logs-response.json. Without this field, an auditor reading the trail cannot tell whetherstatus: "approved" with critical findingreflects a deliberate advisory posture or a governance agent that was inauditmode and could never have blocked anything.Non-breaking justification: adds an optional field to an experimental schema (
x-status: "experimental"); existing producers that omit the field remain valid, existing consumers that skip unknown fields are unaffected.Scope note:
governed_actions[].modewas deliberately deferred — mode can change mid-lifecycle for an action, making a singlemodevalue on the aggregate ambiguous. That shape decision needs a separate issue. Thesummarymode-history suggestion from the issue is similarly deferred.Spec note:
modeis a governance-agent runtime configuration value, not a buyer-declared plan field.sync-plans-request.jsonhas nomodefield; governance agents populate this from their internal state atcheck_governancetime, analogous toplan_hash.Pre-PR review:
$refpath consistent with peer enum refs (outcome,purchase_type);x-entityabsence correct for enum-typed fields; field placement logicaldtfield is prior art for same optional-at-check-time patternNits (not blocking):
governance-mode.jsonlacksx-status: "experimental"— it's the first enum used exclusively in an experimental response; consider annotating before schema graduationgoverned_actions[].modeis eventually added, the entries-levelmodedescription should clarify it reflects mode at check time, not action-lifecycle modeMilestone: 3.1.0
No patch branch (
3.0.x) exists — this targetsmainfor 3.1.0.Session: https://claude.ai/code/session_0185Q8ottjpU8xDRzXQ5aJwx
Generated by Claude Code