Skip to content

feat(schema): add mode to get_plan_audit_logs audit entries#3160

Merged
bokelley merged 2 commits intomainfrom
claude/issue-3156-audit-log-mode-on-entries
Apr 25, 2026
Merged

feat(schema): add mode to get_plan_audit_logs audit entries#3160
bokelley merged 2 commits intomainfrom
claude/issue-3156-audit-log-mode-on-entries

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Closes #3156

Adds an optional mode field ($ref: /schemas/enums/governance-mode.json) to entries[] items in get-plan-audit-logs-response.json. Without this field, an auditor reading the trail cannot tell whether status: "approved" with critical finding reflects a deliberate advisory posture or a governance agent that was in audit mode 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[].mode was deliberately deferred — mode can change mid-lifecycle for an action, making a single mode value on the aggregate ambiguous. That shape decision needs a separate issue. The summary mode-history suggestion from the issue is similarly deferred.

Spec note: mode is a governance-agent runtime configuration value, not a buyer-declared plan field. sync-plans-request.json has no mode field; governance agents populate this from their internal state at check_governance time, analogous to plan_hash.

Pre-PR review:

  • code-reviewer: approved — no blockers; $ref path consistent with peer enum refs (outcome, purchase_type); x-entity absence correct for enum-typed fields; field placement logical
  • ad-tech-protocol-expert: approved — non-breaking per experimental-schema policy; description updated to RFC 2119 SHOULD language per reviewer recommendation; IAB OpenRTB 3.0 Audit dt field is prior art for same optional-at-check-time pattern

Nits (not blocking):

  • governance-mode.json lacks x-status: "experimental" — it's the first enum used exclusively in an experimental response; consider annotating before schema graduation
  • When governed_actions[].mode is eventually added, the entries-level mode description should clarify it reflects mode at check time, not action-lifecycle mode

Milestone: 3.1.0
No patch branch (3.0.x) exists — this targets main for 3.1.0.

Session: https://claude.ai/code/session_0185Q8ottjpU8xDRzXQ5aJwx


Generated by Claude Code

@bokelley bokelley added this to the 3.1.0 milestone Apr 25, 2026
@bokelley bokelley marked this pull request as ready for review April 25, 2026 15:08
claude and others added 2 commits April 25, 2026 11:28
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>
@bokelley bokelley force-pushed the claude/issue-3156-audit-log-mode-on-entries branch from d3327a7 to 3f01fb9 Compare April 25, 2026 15:29
@bokelley bokelley merged commit ae7eae2 into main Apr 25, 2026
17 checks passed
@bokelley bokelley deleted the claude/issue-3156-audit-log-mode-on-entries branch April 25, 2026 15:37
@github-actions github-actions Bot mentioned this pull request Apr 25, 2026
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>
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>
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.

get_plan_audit_logs: surface plan.mode on entries and summary

2 participants