Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .claude/skills/api-integration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,16 @@ Append tool reference block after the FRED block (before `## ENRICHMENT PROTOCOL
- tool_name_1, tool_name_2, tool_name_3
```

**Also**: append a per-domain block to the `MCP_FALLBACK_INSTRUCTIONS` template literal at `_promptConstants.js:721`. This teaches subagents what tool prefix maps to the new domain when MCP routing falls back. Format:

```
**{domain}** — `mcp__{domain}__*` (e.g., `mcp__{domain}__search_items`)
```

- [ ] Tool count matches actual number of schemas
- [ ] Domain name matches DOMAIN_GROUPS key
- [ ] Tool names match schema names exactly
- [ ] `MCP_FALLBACK_INSTRUCTIONS` block present at `_promptConstants.js:721`

### 3.6 Client Registry — `src/server/clientRegistry.js`

Expand Down Expand Up @@ -368,6 +375,29 @@ Three updates:
- [ ] All `expectedCount` sites updated
- [ ] All subagent assertion arrays updated

### 3.8 Feature Flag Registration (when API is gated)

When the new API requires runtime gating (e.g., FMP_ENABLED, EMBEDDING_PERSISTENCE pattern), register the flag in two places:

1. **`super-legal-mcp-refactored/flags.env`** — append the flag near related flags (e.g., near `FMP_ENABLED`):
```bash
{SERVICE}_ENABLED=false
```
2. **`super-legal-mcp-refactored/src/config/featureFlags.js`** — export from the canonical block. Pattern (matching FMP):
```javascript
{SERVICE}_ENABLED: process.env.{SERVICE}_ENABLED === 'true',
```

When gated:
- `domainMcpServers.js` `DOMAIN_GROUPS` — wrap the domain key in a conditional spread: `...(featureFlags.{SERVICE}_ENABLED ? { '{domain}': {Name}Tools } : {})`
- `toolDefinitions.js` `allTools` — same conditional spread pattern (`...(featureFlags.{SERVICE}_ENABLED ? {Name}Tools : [])`)
- `clientRegistry.js` slot — conditional construction: `...(featureFlags.{SERVICE}_ENABLED ? { {slotName}: new {Name}HybridClient(...) } : {})`

- [ ] Flag declared in `flags.env`
- [ ] Flag exported in `featureFlags.js`
- [ ] All conditional spreads wired in `domainMcpServers.js`, `toolDefinitions.js`, `clientRegistry.js`
- [ ] Default value is `false` (opt-in) unless flag is universal

---

## Phase 4: Testing (MUST PASS before merge)
Expand Down
4 changes: 3 additions & 1 deletion .claude/skills/client-offboarding/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,15 @@ All exported via `psql COPY TO STDOUT` + gzip to `gs://super-legal-worm-{client_

### Phase 4: Final Report

**Step 15**: Generate offboarding report
**Step 15**: Generate offboarding report (markdown emitted to stdout + saved to `~/.aperture/offboarding-{client_id}-{date}.md`)
- Client ID, offboarding date, operator
- Resources deleted (with timestamps)
- Archives created (with GCS paths + checksums)
- Remaining resources (WORM bucket — retained for compliance)
- Final cost estimate (last 30 days billing for this client's resources)

**Future enhancement (deferred)**: PDF rendering via pandoc (already used elsewhere in repo for `aperture-demo-preview.pdf`). Markdown output is the canonical artifact today; pandoc wiring is a 1-command future addition (`pandoc offboarding-{client_id}.md -o offboarding-{client_id}.pdf --pdf-engine=xelatex`). Operator can run manually post-offboarding.

## Resource Naming Convention (matches provisioner)

| Resource | Pattern | Action |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,61 @@ def main():
"remediation": "",
})

# D10.4 — new SSE event_types that drive transcript_events / frontend
# rendering must appear in (a) transcriptDBBridge.js allowlist (or
# equivalent persistence path) AND (b) frontend handleStreamEvent switch.
import os as _os
repo_root_p = _os.environ.get("REPO_ROOT") or ""
if not repo_root_p:
cur_p = _os.path.abspath(_os.getcwd())
for _ in range(10):
if _os.path.isdir(_os.path.join(cur_p, "super-legal-mcp-refactored")):
repo_root_p = cur_p
break
cur_p = _os.path.dirname(cur_p)
transcript_paths = [
_os.path.join(repo_root_p, "super-legal-mcp-refactored", "src", "utils", "transcriptDBBridge.js"),
_os.path.join(repo_root_p, "super-legal-mcp-refactored", "src", "utils", "transcriptPersistence.js"),
]
frontend_path = _os.path.join(repo_root_p, "super-legal-mcp-refactored", "test", "react-frontend", "app.js")
transcript_text = ""
for p in transcript_paths:
if _os.path.isfile(p):
try:
transcript_text += "\n" + open(p, errors="replace").read()
except OSError:
pass
frontend_text = ""
if _os.path.isfile(frontend_path):
try:
frontend_text = open(frontend_path, errors="replace").read()
except OSError:
frontend_text = ""

for event_type in s.get("event_types") or []:
# Skip the synthetic types (already handled above)
if event_type in SYNTHETIC_TYPES:
continue
in_transcript = transcript_text and (f"'{event_type}'" in transcript_text or f'"{event_type}"' in transcript_text)
in_frontend = frontend_text and (f"case '{event_type}'" in frontend_text or f'case "{event_type}"' in frontend_text)
if not transcript_text and not frontend_text:
# Neither file accessible; skip silently (covered by D10.1 above)
continue
if not in_transcript:
findings.append({
"dimension": "D10", "status": "WARNING",
"check": f"D10.4 event_type '{event_type}' transcript persistence",
"message": f"event_type '{event_type}' not found as a string literal in transcriptDBBridge.js / transcriptPersistence.js — may be silently dropped from transcript_events",
"remediation": f"Add '{event_type}' to the transcript-event allowlist in transcriptDBBridge.js (or equivalent persistence file).",
})
if not in_frontend:
findings.append({
"dimension": "D10", "status": "WARNING",
"check": f"D10.4 event_type '{event_type}' frontend handler",
"message": f"event_type '{event_type}' not handled in test/react-frontend/app.js handleStreamEvent switch — frontend will ignore it",
"remediation": f"Add `case '{event_type}': ...` branch in handleStreamEvent (app.js).",
})

emit_findings("D10", findings)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,48 @@ def main():
"remediation": "Confirm the producing path triggers embeddingService.chunkByHeaders() with EMBEDDING_PERSISTENCE=true.",
})

# D5.4 — new tables that look like report-derived content should have an
# embedding write path wired in hookDBBridge.js (chunkByHeaders call).
# Without this check, an embedding-friendly table can ship with no producer.
import os, re as _re
repo_root_p = os.environ.get("REPO_ROOT", "")
if not repo_root_p:
# Best-effort: walk up from CWD
cur_p = os.path.abspath(os.getcwd())
for _ in range(10):
if os.path.isdir(os.path.join(cur_p, "super-legal-mcp-refactored")):
repo_root_p = cur_p
break
cur_p = os.path.dirname(cur_p)
bridge_path = os.path.join(repo_root_p, "super-legal-mcp-refactored", "src", "utils", "hookDBBridge.js")
bridge_text = ""
if os.path.isfile(bridge_path):
try:
bridge_text = open(bridge_path, errors="replace").read()
except OSError:
bridge_text = ""
for table in s.get("tables") or []:
# Heuristic: tables suffixed _embeddings / _chunks always need a
# producer; tables ending in _reports / _content / _memos are
# candidates for the report_embeddings flow
report_like = _re.search(r"(_embeddings|_chunks|_reports|_content|_memos|_artifacts)$", table)
if not report_like:
continue
if bridge_text and table in bridge_text and "chunkByHeaders" in bridge_text:
findings.append({
"dimension": "D5", "status": "WARNING",
"check": f"D5.4 embedding write-path for '{table}'",
"message": f"table '{table}' looks embeddable; hookDBBridge.js mentions both — verify chunkByHeaders() runs against rows from this table",
"remediation": "Manually trace the call site in hookDBBridge.js. Confirm INSERT to {table} triggers an embeddingService.chunkByHeaders() call.",
})
else:
findings.append({
"dimension": "D5", "status": "FAILED",
"check": f"D5.4 embedding write-path for '{table}'",
"message": f"new table '{table}' looks embeddable (suffix matches /_(embeddings|chunks|reports|content|memos|artifacts)$/) but no chunkByHeaders() call references it in hookDBBridge.js",
"remediation": f"Add an embedding write path in src/utils/hookDBBridge.js: when INSERT to {table} happens, call embeddingService.chunkByHeaders(content, {{table:'{table}'}}). Gated behind EMBEDDING_PERSISTENCE=true.",
})

emit_findings("D5", findings)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,50 @@ def main():
"remediation": "Run post-deploy-verify Tier 2 t3-bridge-metadata-git-sha.sql + t3-code-execution-models.sql after deploy. If git_sha='unknown', deploy.sh missed --build-arg COMMIT_SHA=$(git rev-parse HEAD).",
})

# D6.5 — KG-relevant subagents must populate kg_provenance.source_hash
# from upstream source_writes (Wave 2 provenance bridge architecture).
import os as _os, re as _re
repo_root_p = _os.environ.get("REPO_ROOT") or ""
if not repo_root_p:
cur_p = _os.path.abspath(_os.getcwd())
for _ in range(10):
if _os.path.isdir(_os.path.join(cur_p, "super-legal-mcp-refactored")):
repo_root_p = cur_p
break
cur_p = _os.path.dirname(cur_p)
kg_paths = [
_os.path.join(repo_root_p, "super-legal-mcp-refactored", "src", "utils", "knowledgeGraphExtractor.js"),
_os.path.join(repo_root_p, "super-legal-mcp-refactored", "src", "utils", "kgService.js"),
_os.path.join(repo_root_p, "super-legal-mcp-refactored", "src", "utils", "kgWrite.js"),
]
kg_text = ""
for p in kg_paths:
if _os.path.isfile(p):
try:
kg_text += "\n" + open(p, errors="replace").read()
except OSError:
pass
for agent in s.get("agent_types") or []:
if not kg_text:
break
# If the agent is referenced by KG-write paths, source_hash must be populated
agent_in_kg = agent in kg_text or _re.search(rf"agent[_\s]?type\s*[:=]\s*['\"]?{_re.escape(agent)}", kg_text)
if agent_in_kg:
if "source_hash" in kg_text and "kg_provenance" in kg_text:
findings.append({
"dimension": "D6", "status": "PASSED",
"check": f"D6.5 KG provenance for agent '{agent}'",
"message": f"agent '{agent}' appears in KG-write path; kg_provenance.source_hash populated",
"remediation": "",
})
else:
findings.append({
"dimension": "D6", "status": "WARNING",
"check": f"D6.5 KG provenance for agent '{agent}'",
"message": f"agent '{agent}' is KG-relevant but kg_provenance.source_hash population is not visible in knowledgeGraphExtractor.js / kgService.js / kgWrite.js",
"remediation": "Confirm KG INSERT path writes source_hash from upstream source_writes (Wave 2 provenance bridge — KG operates on reports, not raw sources).",
})

emit_findings("D6", findings)


Expand Down
92 changes: 92 additions & 0 deletions .claude/skills/sdk-upgrade/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
name: sdk-upgrade
description: Automate Anthropic SDK version bumps for Super Legal MCP. Fetches CHANGELOG diff, audits all call sites for breaking changes, validates beta-token compatibility, runs the SDK regression test suite, and emits a one-page operator report. Replaces the ½-day manual loop Edwin has done 5+ times since 0.2.47. Use when bumping `@anthropic-ai/claude-agent-sdk` or `@anthropic-ai/sdk` versions. Triggers — sdk upgrade, anthropic sdk bump, agent sdk update, sdk version bump, /sdk-upgrade. Supports flags — --to <version>, --check (dry-run), --regression-test (re-run 14 SDK tests).
---

# SDK Upgrade — Anthropic SDK Version Bump

## Workflow

```bash
/sdk-upgrade --check # dry-run audit only — no package.json changes
/sdk-upgrade --to <version> # full upgrade flow (fetch CHANGELOG → audit → bump → test)
/sdk-upgrade --regression-test # re-run 14 SDK test files against current version
```

## What it does

1. **Fetch CHANGELOG** for the version delta via `gh api repos/anthropics/claude-agent-sdk-typescript/releases` and `npm view @anthropic-ai/claude-agent-sdk versions`.
2. **Audit call sites** — greps the 5 known SDK consumer files for usage of API symbols that changed in the delta (e.g., `agentQuery`, `agentProgressSummaries`, `settingSources`, `output_config`, `maxThinkingTokens`).
3. **Validate beta tokens** — checks 5 active beta headers (`context-1m-2025-08-07`, `interleaved-thinking-2025-05-14`, `effort-2025-11-24`, `files-api-2025-04-14`, `code_execution_20250825`) against the new version's release notes for deprecation/graduation status.
4. **Bump versions** in `super-legal-mcp-refactored/package.json` (both `@anthropic-ai/claude-agent-sdk` and `@anthropic-ai/sdk` peer dep).
5. **Run regression test suite** — 14 SDK tests under `test/sdk/` (full list in `references/call-sites.md`).
6. **Emit operator report** — markdown summary with breaking-change matrix, beta-token diff, test pass/fail, and next-step remediation.

## Pre-flight

Required: `python3`, `gh`, `npm`, `node`. The skill reads code locally (no `gcloud` needed).

## Current state (baseline)

Verified 2026-05-07:
- `@anthropic-ai/claude-agent-sdk`: `0.2.119` (exact pin)
- `@anthropic-ai/sdk`: `^0.86.1`
- `zod`: `^4.3.6`

## Known regressions to check (per upgrade)

GitHub issues (Number531/Legal-API):
- **#25** — `maxThinkingTokens` breaks all hooks/streaming on Agent SDK path (still open; commented out in current code)
- **#14** — `defer_loading` for Agent SDK (blocked on upstream)
- **#210** — Agent/Task tool broken in `query()` mode (resolved 0.2.70+)
- **#40** — `task_progress` upstream shipping status (still pending as of 0.2.119)
- **#66** — MCP Protocol SubagentStart race (resolved 0.2.97)
- **#79** — SDK 0.2.119 upgrade notes

## Output format

```
## SDK Upgrade Report
From: 0.2.119 → To: 0.2.121
Timestamp: 2026-05-07T...

### CHANGELOG delta (2 versions)
- 0.2.120: ...
- 0.2.121: ...

### Call site audit
✓ agentStreamHandler.js:280-320 — agentQuery shape unchanged
✓ p0Orchestrator.js:106-135 — settingSources accepted
⚠ codeExecutionBridge.js:325 — files-api-2025-04-14 now graduated; consider dropping beta header

### Beta tokens
✓ context-1m-2025-08-07 — still required
⚠ interleaved-thinking-2025-05-14 — DEPRECATED on 4.6 per CHANGELOG
✓ effort-2025-11-24 — functional
✓ files-api-2025-04-14 — graduated; safe to drop in next major
✓ code_execution_20250825 — server-tool unchanged

### Regression tests (14 files)
✓ agent-stream-handler.test.js
✓ p0-orchestrator.test.js
✓ ... (12 more)

### Recommended next steps
- [ ] Drop interleaved-thinking-2025-05-14 from agentQuery betas if no 4.5 fallback needed
- [ ] Open follow-up issue if files-api graduation breaks existing chart pipeline
```

Exit codes: `0` = clean, `1` = warnings (review before merge), `2` = call-site break or test failure.

## Sequencing

Run **before** `/deploy`. After upgrade, `/post-deploy-verify` Tier 2 covers runtime regression (FMP tools, code-execution models, Cloud Trace).

## Read-only guarantee

The skill never:
- Mutates code other than `package.json` version pin (gated behind `--to`, NOT `--check`)
- Runs the live container or hits production
- Auto-rolls back

It reports what changed; operator decides remediation.
46 changes: 46 additions & 0 deletions .claude/skills/sdk-upgrade/references/call-sites.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# SDK Call Sites — Truth Map

7 source files audited per upgrade. Verified 2026-05-07 against `0.2.119`.

## Agent SDK consumers (3)

| File | Lines | Notes |
|------|-------|-------|
| `src/server/agentStreamHandler.js` | 280-320 | Main `agentQuery` invocation; betas at 296-298; `settingSources: []` at 315 |
| `src/server/p0Orchestrator.js` | 100-135 | `for await … agentQuery({...})` at 106; `agentProgressSummaries: true` at 113 |
| `src/server/claude-sdk-server.js` | 284-310 | Constraints comment block; nearby orchestrator config |

## Anthropic SDK (Messages API) consumers (2)

| File | Lines | Notes |
|------|-------|-------|
| `src/tools/codeExecutionBridge.js` | 325, 352, 484 | Standard `client.messages.create` path; `code_execution_20250825` server-tool |
| `src/utils/skillsRequestBuilder.js` | 41, 52, 55 | Beta header builder for skills |

## Hook lifecycle (2)

| File | Lines | Notes |
|------|-------|-------|
| `src/utils/sdkHooks.js` | exports | `sdkHooksConfig` lifecycle handlers |
| `src/utils/hookDBBridge.js` | 1739-1750 | `HOOKS_TO_BRIDGE` array — must verify on upgrade |

## Test files (14)

Re-run via `test-harness.sh` after every upgrade:

```
test/sdk/agent-stream-handler.test.js
test/sdk/p0-orchestrator.test.js
test/sdk/subagents.test.js
test/sdk/subagents-e2e.test.js
test/sdk/code-execution-bridge.test.js
test/sdk/hookDBBridge.test.js
test/sdk/streaming-events.test.js
test/sdk/thinking-preservation.test.js
test/sdk/structured-outputs.test.js
test/sdk/domain-mcp-servers.test.js
test/sdk/skills-headers.test.js
test/sdk/prompt-caching.test.js
test/sdk/stream-context.test.js
test/sdk/server-refactor-regression.test.js
```
Loading