Protocol Wealth — Cross-Repo CHANGELOG
Cross-repo log of merged PRs, filed issues, decisions, and shared artifacts for the active PW estate (pw-api, pw-os-v2, pw-portal-v2, pw-website, pw-infrastructure, pwos-core, nexus-core, pw-learnai, shared). Newest first. Scoped to anything cross-cutting; per-repo CHANGELOGs (where present) are authoritative for repo-internal detail. The status-doc CHANGELOG at
status/CHANGELOG.mdtracksPW-STATUS-AND-CAPABILITIES-*.mdrevisions only — separate ledger.Style: No personal names; refer to roles (CTO/CISO, CCO, CIO, operator, operator). High-level summaries; PR tables retained but narrative kept compact. Aggregate PR counts over specific PR numbers; outcome statements over process detail; architectural summaries over file-level changes; quantified outcomes preferred. Formal sign-off provenance (Rule 204-2 / 17a-4 books-and-records) lives in
shared/compliance/cco-approvals/records where names + signatures are required for audit; CHANGELOG references those records by path, not by signatory name. The same no-names policy applies toshared/strategy/*,shared/compliance/COMPLIANCE-CHANGELOG.md, memory artifacts, and all public surfaces. Substantive-obligation records (cco-approvals, access-control-register, governance/, Form ADV) RETAIN names per Rule 17a-4 / Rule 206(4)-7 / SEC ADV substantive provenance requirements. See memory artifactschangelog-style-no-personal-names-high-level-summary+role-only-no-personal-names-policy-scope-extended-2026-05-19for the complete scope inventory + two-tier provenance framework. Older entries (pre-2026-05-19) bulk role-substituted without full restructuring; full-rewrite future-only.
2026-05-30 (Onchain explorer — Solana lending/DeFi via Octav)
The advisor /onchain explorer now surfaces Solana lending / LP / vault positions via Octav (pw-api; no migration, no new infra). Operator decision after research: DeBank does NOT support Solana, and a keyless Kamino-only build was abandoned mid-flight; Octav — the complete cross-protocol source pw-onchain already integrated — is used on-demand with a bi-weekly (14-day) per-address cache, so a wallet hits the paid API at most once per two weeks (an ad-hoc lookup of a never-seen address still costs ~1 credit on first view). No infra step was needed — the OCTAV_API_KEY secret was already mounted in pw-api's Cloud Run (previously "no consumer code"). New Octav client ports pw-onchain's mapper (assetByProtocols → one position per protocol, USD value + protocol name + type) and wires into the Solana tracker as a best-effort add-on (empty when the key is unset, so key-less deploys + tests are unaffected). Double-count guard: Octav's wallet pseudo-protocol (token balances) and ALL staking-ish entries are dropped — native + LST staking is owned by the free native detection (shipped earlier the same day); Octav supplies only lending/borrowing/LP/farming/vault/perpetuals. A classifier bug was caught in test (it typed a solend_borrow protocol as lending because "solend" contains "lend" — fixed by checking borrow first). +8 unit tests. To verify post-deploy: the exact Octav response nesting + Solana chain id + borrow-value sign against a real Solana-lending wallet; the mapper refines if the structure is richer than pw-onchain's flat read.
2026-05-30 (Onchain explorer — Solana native-stake APY)
Native Solana stake positions now carry an estimated APY in the advisor /onchain explorer (pw-api; no migration, no new infra). Closes the operator's "I don't see an APY" gap: the liquid-staking-token path already showed Sanctum APYs, but native stake accounts (Marinade Native / Jupiter / Figment validators) showed none. Added a best-effort, 1h-cached, wallet-independent fetch of getInflationRate + getSupply + getVoteAccounts → a network base APY (inflation / staked_pct) and a per-validator commission map; each native-stake position's APY is then base_apy × (100 − commission)/100, using the position's representative validator commission (faithful to pw-onchain's calc that powers onchainstatement.com/staking). Fetched only when a wallet actually holds stake accounts, so stake-less lookups don't pay the heavy validator-set call; any failure degrades to a base-rate fallback rather than throwing. Validated against the operator's live wallet — Marinade Native / Jupiter / Figment now read ~5.2–5.6%, matching the legacy statement. Also completed the validator-name table (Figment etc. now render by name, not a raw vote pubkey). Reference-only port of pw-onchain _get_staking_data; verified by an adversarial workflow pass before build. Decided next: Solana lending positions — DeBank confirmed to NOT support Solana; Kamino's keyless public API is the verified primary candidate; Octav (paid, currently emergency-only) is the complete-coverage alternative — source/refresh-cadence is an operator decision pending.
2026-05-30 (Onchain explorer — Solana staking/LST detection)
The Solana tracker now detects staking and surfaces it as DeFi positions in the advisor /onchain explorer (pw-api + pw-os-v2; no migration, no new infra). Operator ask: proceed with detecting Solana staking/LST. Delivered two best-effort paths on SolanaTracker (a staking-fetch failure surfaces holdings without staking rather than throwing — staking is supplementary to the core holdings): (1) liquid-staking tokens — held LST mints (mSOL/jitoSOL/bSOL/JupSOL) are reclassified out of token balances into Staking positions (so the portfolio total isn't double-counted) carrying a live APY from the keyless Sanctum batch API; (2) native stake accounts — queried via getProgramAccounts on the Stake program by both authorized-withdrawer and authorized-staker offsets (Marinade Native sets the wallet as staker, not withdrawer, so one offset alone misses it), deduped, and grouped by platform family (Marinade Native / Jito collapse across validators) or per-validator. Sanctum API gotcha codified: the /v1/apy/latest endpoint zeroes at epoch boundaries (verified returning 0.0 for major LSTs on the build day), so the integration uses /v1/apy/inception (stable since-inception average) to avoid rendering a misleading 0% yield. The explorer UI surfaces the APY on staking-position rows (additive, graceful for positions without one). Native-stake APY is left for a follow-on (validator-commission-derived, needs the heavy getVoteAccounts call). Reference-only port of pw-onchain trackers/solana.py; +13 pw-api unit tests, all green. Closes the "Solana LST/staking" follow-on flagged when the multi-chain explorer shipped earlier the same day.
2026-05-30 (Onchain explorer — multi-chain + auto-detecting; Solana/BTC/XRP)
The advisor /onchain explorer became multi-chain with address auto-detection (pw-api #301 LIVE + pw-os-v2 #425; no migration, no new infra). Operator ask: add Solana (Jupiter) + auto-detect the chain + cover every chain we can support + drop the manual chains input + prompt the advisor when undetectable. Delivered: a detectAddressChain() classifier (EVM / Sui / Solana / Algorand / Bitcoin / XRP, ordering the prefix-bearing families ahead of the base58 fallthrough), three new native BaseTracker implementations — Solana (native SOL + SPL via the QuickNode Solana RPC getTokenAccountsByOwner/getBalance, priced by the keyless Jupiter Price v3), Bitcoin (keyless Blockstream + DeFiLlama price), XRP (XRPL account_info via the QuickNode XRP node + public-cluster fallback + DeFiLlama price) — and a summary-route refactor that drops the required chains= param: omit it and the address shape selects the chain, an unrecognized shape returns needs_chain_hint (HTTP 200) so the UI prompts the advisor with a manual chain picker, and chains= still works as an explicit override. The explorer UI drops the chains input, sends the address as-entered (no lowercasing — Solana/BTC/XRP are case-sensitive), and shows a detected-chain badge + override. Why Jupiter alone wasn't enough: Jupiter doesn't enumerate a wallet's holdings — that's a Solana RPC call — but pw-api already had a full QuickNode Solana node (provisioned for KYW), so holdings work with zero new infra; Jupiter supplies the USD prices. Reuses the existing QuickNode Solana/XRP endpoints; Jupiter / Blockstream / DeFiLlama are keyless. Reference-only patterns from nexus-core (app/chain.py, data/onchain/jupiter.py) + pw-onchain (trackers/{solana,bitcoin,xrp}.py), reimplemented natively in pw-api per their charter. +26 tests; all green. Follow-on: Jupiter Token API (keyed) for full SPL symbols, Solana LST/staking, a dedicated Solana RPC endpoint.
2026-05-30 (Phase-3 pw-onchain Slice 4 PR-5b/5c — reader cutover BUILT, HELD for review)
The Fly-proxy holdings-reader cutover is built and open as two HELD PRs (pw-api #300 + pw-os-v2 #424; no auto-merge; no migration). This swaps the live client-portfolio holdings reader, so it awaits operator review + dogfood before merge. Design is deliberately safe — opt-in native + Fly fallback: the native reader aggregates a live tracker fan-out across a client's tracked EVM/Flare wallets (keyed by wallets.client_id) into the existing section shape, but returns null (opt-out) when the client has no native wallets, so the legacy Fly group proxy still serves every un-migrated client — no existing client's holdings change until wallets are seeded. Both read paths cut over (the advisor REST /portfolio/client-view + the tRPC portfolio.getClientView, now sequential since the native read shares the request DB client). A new /portfolio/onchain-history endpoint reads the entity_type='client' rollup snapshots (PR-5a) for the client-card balance-over-time chart. The client card (5c) flips "Source: pw-onchain" → "pw-api", drops the clients.metadata.onchain_group_name empty-state instruction (native keys off wallets.client_id), adds the history chart + timeframe picker, and fixes a React key collision in the holdings wallet list. The operator chose a live tracker call for the current total + the snapshot rollups for history. All green locally (typecheck/lint/build; +7 unit/BFF tests; a pw-api integration spec runs in CI). Merge order: #300 then #424; Fly stays as fallback until native is proven. This completes the Slice-4 build — only the operator's review/merge of the cutover remains.
2026-05-30 (Phase-3 pw-onchain Slice 4 PR-5a — writer client/group rollups)
The daily snapshot writer now emits entity_type='client'/'group' rollup rows (pw-api; additive, no migration). The foundation for the held reader cutover (PR-5b/5c): after each tenant's wallet rows commit for the day, the writer recomputes that tenant's client + group daily_snapshots rollups by summing today's wallet rows joined to wallets for client_id / group_name (daily_pnl vs the prior-day rollup). It runs in its own transaction so a rollup failure never rolls back the wallet snapshots, is idempotent, and recomputes from all of today's rows so it converges across cursor batches. The daily_snapshots CHECK already admits client/group entity_type, so no migration. Nothing reads these rows yet (the Fly proxy still serves holdings), so this landed safely ahead of the cutover. +2 integration cases (green in CI's Migration dry-run). The remaining reader cutover (native loadOnchainSection off wallets.client_id with Fly fallback + the client-card history chart + set-group-from-UI + the "Source" label flip) stays HELD for operator sign-off, with the operator's choice locked: a live tracker call for the current total + the snapshot rollups for history.
2026-05-30 (Phase-3 pw-onchain Slice 4 PR-4 — lookup_wallet_history chat tool)
Address-keyed onchain history chat tool shipped (pw-os-v2; the last decoupled Slice-4 piece). lookup_wallet_history — advisor-gated, read-only — wraps the pw-api performance route (daily_snapshots) to return a daily total-USD series with the net change over a window (default 30 days) for a single EVM address; empty for untracked addresses (graceful hint to lookup_wallet_portfolio). Reconciled against the existing address-keyed tools: the originally-planned get_wallet_balance was dropped as redundant (lookup_wallet_portfolio already returns an address's live balance), so lookup_wallet_history is the genuinely-new time-series surface. Pure formatter unit-tested (5 cases); registers cleanly into the tool registry. With this, Slice-4 PRs 1–4 are live; only the PR-5 Fly-proxy holdings-reader cutover remains, held for operator sign-off (approach locked: the daily writer also emits entity_type='client'/'group' rollup rows — no migration — then the native reader keys off wallets.client_id).
2026-05-30 (Phase-3 pw-onchain Slice 4 PRs 2+3 — duplicate-address lookup + Manage-Wallets UX)
Manage-Wallets refinements + a compliance string fix shipped (pw-api PR-2 + pw-os-v2 PR-3; no migration). Folds operator feedback Part A item 4 into the inline wallet manager on the advisor client portfolio Onchain card. pw-api (PR-2): new read-only GET /v1/internal/onchain/wallets/lookup matches an address across both the wallets table (tracked + soft-deleted) and the client_wallets KYW-link table — matched on the address (case-insensitive for 0x-hex, verbatim for base58/bech32), since the two tables label chains differently — returning { already_tracked, wallet_matches[], client_wallet_matches[] }; audited as a duplicate_lookup view. pw-os-v2 (PR-3): the add-a-wallet form now offers a single labeled "EVM — Ethereum mainnet + all DeBank chains" choice (sub-chain field dropped) plus solana/bitcoin/xrp/flare; a select-existing-or-"add new" group combobox seeded from a new groups BFF proxy; a per-wallet Refresh button (+ auto-fire on add) that pulls a live balance via the explorer summary route (EVM/Flare only); and duplicate-address detection on add that warns — without blocking, since joint custody is legitimate — naming where the address is already tracked. Compliance: the client crypto-toggle confirm string no longer names the decommissioned Chainalysis vendor or legacy "pw-onchain reads"; it now reads "Scorechain sanctions + KYW screening … native onchain holdings reads." Two new advisor BFF proxies (groups + lookup; pw-api owns the audit) with 3 unit tests; a dedicated pw-api integration spec (8 cases, green in CI). The UI degrades gracefully if the lookup endpoint isn't deployed yet (advisory pre-check + empty-group fallback). Slice-4 remainder: lookup_wallet_history chat tool (PR-4); Fly-proxy holdings-reader cutover (PR-5, held for operator sign-off).
2026-05-30 (Phase-3 pw-onchain Slice 4 PR-1 — /onchain address explorer)
/onchain advisor address explorer shipped (pw-os-v2; decoupled, no migration). First of four Slice-4 PRs. Paste any EVM address → live multi-chain balance + per-chain / top-token / DeFi-position breakdown (pw-api onchain portfolio summary route) plus a balance-over-time chart (performance route → daily_snapshots). The previously-disabled /overview "Onchain explorer" hub card is now live; the route is advisor-gated (mirrors /market-data) so cost-bearing DeBank lookups stay off the employee surface. Two read-only BFF proxies (GET /api/advisor/onchain/explorer/{summary,history}, SYSTEM_TENANT_ID + session actor_email injected; pw-api owns the audit) added to the existing onchain BFF router — zero new mount. New reusable inline-SVG BalanceHistoryChart (no charting dependency — to be reused by the Slice-5 client portfolio card). Daily history populates only for tracked wallets with accrued snapshots; untracked addresses still show live holdings, no series. 7 new BFF unit tests; full 1533-test API suite + lint + typecheck green; the explorer ships as its own lazy bundle chunk (main bundle untouched). Slice-4 remainder: Manage-Wallets UX refinements + decommissioned-Chainalysis-confirm-string fix (PR-2/3); lookup_wallet_history chat tool (PR-4 — reconciled, since lookup_wallet_portfolio already covers address-keyed live balance); Fly-proxy holdings-reader cutover (PR-5, HELD for operator sign-off; approach locked = the daily writer also emits entity_type='client'/'group' rollup rows, which the schema already permits with no migration).
2026-05-29 (Friday — Phase-3 pw-onchain Slice 2: daily-balance writer cron)
Phase-3 pw-onchain port — Slice 2 daily-balance writer SHIPPED (pw-api half; pw-api #296). The producer that turns the Slice-3 wallet rows into snapshot history — unblocks the Slice 3.5 self-heal corrector (it now has daily_snapshots to walk) + the Slice 4 history reads. src/lib/onchain-snapshot-writer.ts (runSnapshotWriterPass) clones the proven snapshot-self-healing.ts tick: per-tenant createRequestClient, cursor-resumable over wallet id, duration-budgeted, returns 200 on every reachable outcome. Selects wallets due by next_snapshot_at (chain_type IN ('evm','flare') — the tracker-covered chains; solana/btc/xrp await trackers), calls getEVMTracker/getFlareTracker honoring the throw-on-exhaustion contract (a thrown fetch is a hard fail — no $0 snapshot, next_snapshot_at NOT bumped so the next fire retries), writes per-token wallet_snapshots (is_recent demoted) + an upserted daily_snapshots row (entity_type='wallet', entity_id=<lowercase address>, total = holdings + positions when include_defi, daily_pnl vs the prior daily row), bumps last/next_snapshot_at. Emits onchain.snapshot.written per wallet + onchain.daily_rollup.completed per tick (both pre-declared; the crypto address is PII-redacted in after_state). New cron endpoint POST /v1/internal/cron/onchain/snapshot/run-next (OIDC-only, cursor + batch_size query overrides). Env ONCHAIN_SNAPSHOT_{BATCH_SIZE=50,MAX_DURATION_MS=9min} are code-side .default() — no Cloud Run env mount; only the Cloud Scheduler is new. No migration. 10-case integration suite green against real Postgres in CI (caught + fixed two test bugs pre-merge: a cross-test contamination from the all-due-wallets pass, and an assertion that expected the un-redacted address in the audit after_state). Cloud Scheduler pwllc-prod-onchain-snapshot-tick (pw-infrastructure #222, fires 02:00 UTC — before the 03:00/03:30/04:00 pw-api tick cluster so its fresh daily_snapshots feed the 04:00 self-heal corrector) was APPLIED + smoke-tested GREEN (2026-05-30 03:24Z): operator merged pw-infrastructure #222 → GitHub Actions WIF auto-applied; manual gcloud scheduler jobs run returned HTTP 200 tick_completed (processed:0 — wallets table empty — in 148ms, no silent-401), job ENABLED, next fire 02:00 UTC (smoke-test gate per feedback_scheduler_tick_smoke_test satisfied). Slice 2 LIVE end-to-end; daily_snapshots accrue once wallets are tracked via the Slice-3 surface. Next: Slice 4 (advisor balance + history lookup UI + chat tools).
2026-05-29 (Friday — Phase-3 pw-onchain Slice 3: native onchain wallet CRUD + groups + client assignment)
Phase-3 pw-onchain port — Slice 3 pw-api substrate SHIPPED (pw-api #295). Native wallet management on the wallets table (empty until now), the substrate the Slice 2 daily-balance writer needs before it has rows to snapshot. Replaces the legacy Fly pw-onchain wallet CRUD + the clients.metadata->>'onchain_group_name' linkage hack. New OIDC-gated internal routes: POST /v1/internal/onchain/wallets (create/track — idempotent on (tenant_id, chain_type, address); reactivates a soft-deleted row, 409 on an active dup; sets next_snapshot_at=now() so Slice 2 picks it up; lowercases 0x EVM/flare addresses; validates client_id in-tenant; re-track preserves prior client/group via COALESCE), PATCH /onchain/wallets/:id (update/assign/clear — null clears a field; re-enabling tracking refreshes next_snapshot_at), DELETE /onchain/wallets/:id (reversible soft-delete + untrack), GET /onchain/groups (wallet-group read axis — group_name + wallet/tracking/client counts + client_ids), plus a client_id filter on the existing GET /onchain/wallets. Audit verbs onchain.wallet.{tracked,updated,untracked} (.updated is new; all TypeScript-only — audit_log.action is plain TEXT with no CHECK). No migration — the schema was complete at migration 0019 and the group model is the shipped denormalized wallets.group_name string (matches daily_snapshots.entity_type='group'), not a new FK table. All writes touch only pw-api's own wallets table (no upstream-vendor mutation), so they ride the internal REST surface alongside the KYW client_wallets link/unlink precedent. 23-case integration suite green against real Postgres in CI (Migration dry-run job); adversarially reviewed pre-merge (the re-track field-clobber footgun was caught + fixed). The Fly-proxy reader cutover (loadOnchainSection → native) is deferred to after Slice 2 populates daily_snapshots. Advisor UI (slice-3 part 2) SHIPPED (pw-os-v2 #420): an OIDC BFF proxy (advisor-onchain-wallets.ts) + an inline "Manage wallets" section in the Onchain card on ClientPortfolioPage (OnchainWalletManager — add/track + edit group/label + soft-delete; client_id-keyed, independent of the Fly-proxy holdings breakdown). UI placement = expand-the-card per operator decision. Slice 3 COMPLETE. Re-sequenced remainder: Slice 2 (daily-balance writer cron) → Slice 4 (advisor balance + history lookup UI + chat tools).
2026-05-29 (Friday — /demo state surfaces deployed; Chainalysis sanctions vendor decommissioned; FMP/Chainalysis internal-doc sweep; Chainalysis fully decommissioned + KYW (Scorechain Risk/QuickNode) LIVE; multi-chain onchain holdings layer LIVE on pw-api; pwos.app/systems cleanup; pw-onchain Phase-3 port started; Tatum onboarded)
/demo state surfaces shipped to pwos.app + pwportal.app. Static state-surface pages served at /demo on both apps (pw-os-v2 #414, pw-portal-v2 #83) with a follow-up scoped-CSP fix (pw-os-v2 #415, pw-portal-v2 #84) so the inline-styled + Google-Fonts pages render under each app's strict global CSP without relaxing any other route. platform-state.md wired as manually-curated chat grounding on both surfaces.
Chainalysis OFAC sanctions source decommissioned (pw-api #287). Confirmed LIVE (not dead) at removal — secret provisioned + mounted, real per-screen OFAC calls OR-combined into the verdict. Sanctions screening is now Scorechain Free Sanctions only (OFAC + OFSI + MOFA + NBCTF). chainalysis_result column + historical rows RETAINED (Rule 204-2 / 17a-4); new rows stop writing it. Terraform secret + env mount DESTROYED (pw-infrastructure #214/#215); dormant 'chainalysis' type-union literals REMOVED (pw-api #288, pw-portal-v2 #85); CCO governance sign-off RECORDED (shared #447). CTO-authorized; CCO-ratified. Client-facing Privacy Policy + subprocessors edits drafted for CCO review (strategy/2026-05-29-cco-draft-chainalysis-removal-octav-refine.md) — NOT published.
KYW (Scorechain Risk via QuickNode) LIVE. Wallet-risk scoring (KYW) is now returning real scores in production — a SEPARATE Scorechain product from Scorechain Free Sanctions (the two are independent). Secrets + mounts wired (pw-infrastructure #216/#217); param-shape fix landed (pw-api #289: sc_getAddressAnalysis takes hash-only). 7 chains live: eth / base / btc / sol / xrp / avax / arbitrum (Arbitrum KYW mount pw-infrastructure #219). Verified returning real scores.
Multi-chain onchain HOLDINGS layer LIVE on pw-api (rev 00286). New tracker layer at src/lib/trackers/: DeBank/EVM (existing) + FlareTracker (pw-api #292, QuickNode native FLR) + TatumTracker (pw-api #293, native HyperEVM / Algorand / Sui via the Tatum gateway, x-api-key). Env mounts (pw-infrastructure #221: QUICKNODE_FLARE_RPC_ENDPOINT + TATUM_API_KEY) + secrets (#220). GET /v1/internal/onchain/portfolio/summary now returns these chains (address-keyed, native-only).
pwos.app/systems cleanup (pw-os-v2 #418). Retired pw-strategies / pw-signals / pw-insights / pw-onchain from the systems surface; onchain is now served by pw-api. nexus-core / nexusmcp.site presented as the live open educational MCP.
Phase-3 pw-onchain PORT started. Slice 0 (systems) + slice 1 (holdings) DONE. nexus-core is REFERENCE-ONLY — build in pw-api, do NOT consume it (its charter is anonymous / educational / no-client-data). Re-sequenced remaining work: slice 3 (wallet rows + groups + client→wallet assignment; wallets table is EMPTY) → slice 2 (daily-balance writer cron) → slice 4 (advisor balance+history lookup UI + chat). Heavy follow-on (tables ported, no writers): statements, cost-basis, tax, LP, staking/yield, eth-risk. Legacy source: /mnt/c/Users/nick/Desktop/PROJECTS/pw/pw-onchain. Plan: memory project_pw_onchain_port_2026_05_29.
Vendor ops. Tatum onboarded (68-chain gateway, Starter plan) as the native-balance source for HyperEVM / Algorand / Sui. DeBank API key rotated.
FMP/Chainalysis internal-doc sweep. Corrected stale current-tense FMP references (PWOS.md, alphavantage.md, emf-canonical.md) and added decommission/staleness banners (architecture/api/chainalysis.md, PW-CACHING-LAYER-ARCHITECTURE.md). FMP was removed 2026-05-14 (ToS commercial-use); MBOUM + SEC EDGAR are the fundamentals sources. Octav confirmed manual emergency-restore-only (out of daily pipeline; no build — YAGNI).
2026-05-28 (Thursday — OSS Strategy v1.2 published; CFP Board fact sheet drafted for Friday Zaniello call; Claude Opus 4.8 frontier swap; PWOS Coordinator Module design + P1 build)
Open Source Strategy v1.2 published to the firm's public surface via pw-website #122. New top-level page at https://protocolwealthllc.com/opensource-strategy carries the canonical hubs / licensing / AI-governance / public-vs-private statement adapted from shared/docs/compliance/opensource-policy.md v1.2. §1.4 HITL Tier 2 canonical phrase preserved verbatim from the post-reconciliation source; §7.4 synthetic-data discipline carried over as the firm-wide commitment. Sanitization scrubbed internal PR numbers, internal authority notes, and shared/strategy/ cross-references; project-management sections (current-state snapshot, roadmap status, implementation phases, risks register, metrics, decision-log) dropped from the published version while the strategic surface (frame, external projects to absorb, license compatibility matrix, attribution discipline) retained. Footer adds a "Open Source Strategy" link sibling to the existing GitHub "Open Source" link; /opensource hero gains a cross-link to the new page. CTO/CISO-autonomous publication of an already-canonical posture; no Marketing Rule §206(4)-1 client-recommendation surface added.
CFP Board fact sheet drafted at shared/strategy/2026-05-29-cfp-board-zaniello-fact-sheet.md (this PR) for the Friday 2026-05-29 3pm Zaniello call. ~1,040 body words across six sections: opening verdict; what PW has built (concrete artifacts — @protocolwealthos/[email protected] + [email protected], 19-package npm scope, Reg-S-P §248.30 inspection-ready, ZDR-enrolled Anthropic workspace); why fiduciary-aligned (HITL Tier 2 + Marketing Rule + Reg-S-P + synthetic-data); what CFP Board could examine (schema + published strategy URL + reference architectures + 2-person intern adopter feedback loop); what PW is asking (directional alignment check + schema-evolution PR invitation, not endorsement); closing dialogue framing. Marked DRAFT — CCO REVIEW PENDING per dispatch; four operator-input slots at the bottom flag (a) specific question(s) to put to CFP Board, (b) CCO review window, (c) pre-read attachments, (d) tone calibration. Phrase A (HITL Tier 2) cited verbatim from opensource-policy.md §1.4; Phrase B (ZDR API-surface) cited verbatim from wisp-ai-posture.md §1 bullet 4.
Claude Opus 4.8 frontier model swap — Anthropic shipped Claude Opus 4.8 on 2026-05-28 as the new flagship; CLAUDE_MODEL_FRONTIER bumped from claude-opus-4-7 to claude-opus-4-8 on pw-os Cloud Run via pw-infrastructure #213, with sibling cost-table row landed via pw-os-v2 #407. Only Opus advanced; Sonnet 4.6 (WORKHORSE) and Haiku 4.5 (LIGHTWEIGHT) unchanged. Zero breaking API changes from 4.7 — identical feature set + 1M context + 128k output + adaptive-thinking-only + $5/$25 per MTok pricing. Anthropic-documented gains for PWOS workloads: better tool triggering (fewer skipped tool calls on the 64-chat-tool surface), better long-horizon agentic coding, better compaction recovery, more reliable per-effort calibration. PWOS coordinator tier (which pins modeOverride='max' → CLAUDE_MODEL_FRONTIER) inherits 4.8 on next Cloud Run revision rollout. Three new 4.8 features (mid-conversation system messages, refusal stop_details.category, fast-mode research preview) deferred to separate follow-on PRs; a dedicated Claude Opus 4.8 research-session prompt is staged at shared/strategy/2026-05-28-claude-opus-4-8-research-session-prompt.md for a focused investigation of 4.8 best practices across PWOS chat and local Claude CLI.
PWOS Coordinator Module — design + P1 MVP end-to-end in one session. Same-day delivery from design proposal through P1 build of the partner-tier orchestrator surface. Design landed via shared #421 — shared/strategy/PWOS-COORDINATOR-MODULE-DESIGN.md 7-section DRAFT proposing P1-P4 phasing; P1 cleared to build as REVIEWABLE-PR (system-prompt edits capability-bearing); P4 (officer-approval substrate) explicitly BLOCKED on CCO design review per §4.P4. Three corrections vs prior session-memory baked into the doc: (a) session_mode enum is advisor/troubleshoot/develop per migration 0038, not produce; (b) pre-existing PR #406 was OPEN not merged; (c) audit-archive lock effectiveTime is 2026-04-17T23:13:26Z, not the 2026-05-02 in memory reference_audit_archive_lock. P1 MVP shipped via pw-os-v2 #409 (CCO+CTO ratified, REVIEWABLE) — POST /api/protected/chat/conversations widened for purpose field with partner-title gate; DEFAULT_TIER_AWARENESS_SNIPPET appended to default system prompt + COORDINATOR_TIER_CONFIRMATION prepended to coordinator system prompt (closes both directions of the self-audit gap surfaced earlier same day where the in-chat model reported "there's no Coordinator in this tool surface"); partner-only "🌐 Start Coordinator Chat" button in sidebar topSlot; read-only Coordinator badge in chat header. PATCH/promote button shipped via pw-os-v2 #411 — PATCH /conversations/:id purpose handling + interactive promote/demote button replacing the read-only badge; updateConversationPurpose() helper added; audit row carries via='promote'|'demote' to distinguish from POST path's via='create_conversation' (CCO examiner pivot is symmetric across all three vectors via action='conversation.purpose.set' AND resource_id=<conv_id>). Sibling attachment expansion via pw-os-v2 #410 — CSV / TSV / JSON added to SUPPORTED_MIMES via the existing text-inline path; new TEXT_INLINE_MIMES export. Sidebar polish via pw-os-v2 #412 — cyan "coordinator" badge in conversation list. Supersession: pw-os-v2 #406 closed as superseded by #411 (couldn't update-branch after #409 landed; conflicts on chat.ts; rebuilt fresh under #411 with same intent + P1 framing). Living-state: shared #422 captured the wave in PWOS-STATE.md RECENT. Carry-forward to next session: P2 (decisions/action-items tracker + Daily Update content surface) needs design memo before build; P3 (ADR review surface) similar; P4 BLOCKED on Adam design review per §4.P4 of the design doc.
Langfuse model registry cleanup (no PR — direct API writes against self-hosted v2.95.11 at langfuse.protocolwealthllc.com). Added claude-opus-4-8 model def at correct $5/$25 per MTok pricing matching PWOS-canonical apps/api/src/lib/claude-cost.ts. Cleaned duplicate claude-opus-4-7 entries (deleted 1 null-tokenizer dup, recreated 1 at correct $5/$25 schema matching the 4.8 def field-for-field — same unit=TOKENS + tokenizerId=claude + tokenizerConfig={} + clean modelName). Pre-cleanup state had 4.7 mis-priced at the OLD pre-PR-7.5b $15/$75 rate (the 3× error PWOS's own claude-cost.ts corrected on 2026-05-11; the Langfuse registry had drifted out of sync). Final state verified: 4 PW custom Claude defs (4.7 / 4.8 / Sonnet 4.6 / Haiku 4.5) all schema-identical + pricing aligned with Anthropic published rates + matchPatterns cover the live PWOS env strings (claude-opus-4-8 / claude-sonnet-4-6 / claude-haiku-4-5-20251001).
Reg S-P / Reg XP June 3, 2026 deadline countdown: 6 days remaining.
2026-05-27 (Tuesday close — P0.K1 KEYSTONE ARC FULLY CLOSED; integration suite caught latent prod bug pre-deploy)
P0.K1 keystone arc closed end-to-end today. Three sequential closes:
P0.K1.next CLOSED 10:29Z — operator re-smoke against the deployed-fixed
pw-api-00273-t2mrevision returned cleantick_started+tick_completed+ HTTP 200 +accounts_processed=0(expected; no Altruist tenant onboarded yet). The 2026-05-26 EOD first-tick RLS-tenant-context throw confirmed fixed.P0.K1.test CLOSED 15:31Z via pw-api
#284— 12-test integration suite shipped attest/integration/positions-ingest-cron.spec.ts: RLS-context regression centerpiece (FIX-HOLDS + BUG-REPRODUCES under FORCE-RLS-engagedpwapi_approle — the pre-deploy guard for#283-class bugs), per-account isolation e2e, content-hash dedup + sentinel-supersedes + immutability trigger, 3-strike retry trail, adapter-auth-failed batch continuation, zero-account graceful exit (the 10:29Z re-smoke shape pinned permanently into CI), cursor pagination over real Postgres. Initial PR landed via Option B (5/12 active + 7 skipped pending production fix).PRODUCTION BUG SURFACED + FIXED via pw-api
#285(merged 16:21Z) — the suite caught a latent TEXT=UUID type-mismatch ataltruist-positions.ts:323that would have thrownoperator does not exist: text = uuidon the first real Altruist account onboarding. Migration 0038 widenedaudit_log.resource_idUUID→TEXT but the subquery still comparedresource_id = ps.id(UUID column). Root cause confirmed via throwaway 3-point probe (commits1fda761add +f5f80b7revert, preserved in history for governance auditability). Fix: one-characterps.id::textcast + defensive explicittenant_id = current_setting('app.tenant_id', true)::uuidfilter in the sweeper SELECT atcron-cursor.ts(defense-in-depth on top of FORCE RLS; protects against role-posture regressions, produces#283-style loud-failure on missing context instead of silent cross-tenant rows). 12/12 orchestration tests green. 1727/1727 unit baseline preserved. Lint + typecheck clean.
Companion strategy doc landed via shared #397 at shared/strategy/2026-05-27-tool-replacement-parity-map.md — sequences the 11 P1-DEPENDENTS against the operator's stated RightCapital/Monarch/Wealthbox cancellation goal (Monarch first/nearest parity, RightCapital second/every-calculator-net-new, Wealthbox last/~12-month parallel-write); Sections 2 + 4 fill-in templates for the operator+partners ~20-minute conversation.
Substantive outcome: pre-deploy integration test (#284) caught a pre-existing production defect (#285) that no manual review or unit-test layer had surfaced — the exact value proposition the dispatch invoked when ordering keystone integration tests as the next CLI build post-#283. The 12-test CI gate now permanently catches future regressions in this code path.
Keystone arc PR audit trail: substrate (pw-api #280, 2026-05-26), cron-tick RLS-context fix (pw-api #283, 2026-05-27 03:13Z), integration centerpiece (pw-api #284, 15:31Z), production cast + sweeper hardening (pw-api #285, 16:21Z).
OSS surface went live today. @protocolwealthos/[email protected] + @protocolwealthos/[email protected] published to npm under nickrygiel maintainer; names locked. pwos-core monorepo now publishes 19 packages total (17 pre-existing + 2 new). disclosure-card is the flagship adoptable-standard artifact for AI-system disclosure — Zod runtime validation + JSON Schema + CI gate for verbatim required-string presence. shared carries the HITL fail-closed gate primitive + SHA-256 provenance hash-chain. Both earned focused sibling-package surfaces (disclosure-card was previously a subpath within shared; the split lets each carry its own README, examples, and adoption surface independently). Friday-ready as the standards artifact for the CFP Board / Zaniello conversation. Workflow gotcha + workaround: the Release workflow's NPM_API_KEY doesn't have write permission to create NEW packages in the @protocolwealthos scope; worked around via manual user-publish from local under maintainer nickrygiel. Non-blocking follow-up: token-fix before the next intended version bump. CTO-autonomous publishing event (no Adam pre-clearance — no client-facing content); CCO informed per cross-firm transparency norm; logged to shared/compliance/COMPLIANCE-CHANGELOG.md.
Reg S-P / Reg XP June 3, 2026 deadline countdown: 6 days remaining. Next: BlockSkunk Phase-0 kickoff call later today (signed engagement, 50% paid).
2026-05-26 (Monday OVERNIGHT close — P0.K1 first-tick RLS bug + fix shipped + DEPLOYED; Schwab dropped; iteration close)
P0.K1 first-tick prod bug + fix shipped + DEPLOYED tonight. First prod smoke-test against the operator-applied pwllc-prod-positions-altruist-tick Cloud Scheduler (pw-infrastructure #205, applied earlier evening) threw "invalid input syntax for type uuid: \"\"" at 6ms — surfaced via the smoke-test gate per memory feedback_scheduler_tick_smoke_test.
Root cause: the sweeper SELECT against the FORCE-RLS-protected custodian_accounts ran via raw opts.pool.connect() with no app.tenant_id session var set. The RLS USING policy current_setting('app.tenant_id', true)::uuid returns '' when missing_ok=true (NOT NULL), and ''::uuid throws. My altruist-positions-tick.ts diverged from the canonical per-tenant-context pattern at snapshot-self-healing.ts:485 which uses createRequestClient(tenantId) to set context BEFORE any RLS-protected query.
Fix: pw-api #283 — per-tenant sweep loop via createRequestClient(tenantId) (default [SYSTEM_TENANT_ID]; multi-tenant generalization via opts.tenantIds); cursor-normalize 3-layer defense-in-depth (cron.ts handler + altruist-positions-tick.ts entry + cron-cursor.ts function entry; new normalizeCursor() pure helper handles null/undefined/'' → null); zero-tenant graceful exit (batch_completed audit still emits using tenantIds[0] so ops dashboard query stays meaningful on idle ticks); explicit $2::uuid cast in SQL; 7 new unit tests in test/unit/positions-ingest-cron-cursor.spec.ts. 1727/1727 unit tests pass; lint + typecheck clean.
Deploy status: PR #283 MERGED at 03:05:43Z; Deploy pw-api completed success at 03:13:21Z; current live revision pw-api-00273-t2m (past the buggy pw-api-00272-gkk). Operator re-smoke against the deployed-fixed revision PENDING tomorrow (NOT re-run in this close-out — re-smoking 00272 would reproduce the bug; must wait for deploy-confirm + re-smoke against 00273-t2m).
Schwab path REFRAMED (operator decision 2026-05-26 EOD): Altruist Open API surfaces Schwab-custodied accounts directly (PW receives Schwab-held assets via the existing source='altruist' adapter); no separate Schwab file-batch adapter needed for visibility. P0.K2 DEMOTED to DEFERRED-LOW in ROADMAP; the keystone substrate already covers Schwab via Altruist ingest. Only open Schwab question is lot-level cost-basis fidelity (synthetic-single-lot fallback handles aggregate-only sources until/unless a direct Schwab feed is wanted). Consequence: tomorrow's first CLI build = P0.K1.test integration tests (incl. RLS-context regression — THE test that would have caught pw-api #283 pre-deploy); THEN operator picks first unblocked P1.D* dependent, NOT a second adapter.
Diagnostic verification report (read-only; no remediation)
| Surface | Method | Result |
|---|---|---|
| Migration 0058 applied in prod | gcloud run jobs executions list --job=pw-api-migrate |
5 successful executions today (latest 03:12:13Z from #283 deploy); substrate physically exists. Direct DB query blocked by private-IP per dispatch direction. |
| 12 keystone audit verbs canonical-shape | static regex check against pw-api/src/lib/audit-actions.ts |
All 12 PASS the 3-segment parseAction regex (fix-forward A from shared #395 fully landed). |
| pw-api ERROR logs (last 6h) | gcloud logging read severity>=ERROR --freshness=6h |
3 first-tick throws (02:47/02:49/02:51 UTC) — ALL the known RLS bug from operator smoke-test. Nothing new/unexplained. |
| pw-os ERROR logs (last 6h) | same | Zero errors. |
Scheduler pwllc-prod-positions-altruist-tick |
gcloud scheduler jobs describe |
ENABLED, schedule 0 12 * * *, time_zone Etc/UTC, URI + SA correct, attemptDeadline 720s. |
Scheduler pw-api-snapshot-self-healing-nightly (didn't get disturbed) |
same | ENABLED, schedule 0 4 * * *. |
Scheduler pwos-market-scan-run-next (didn't get disturbed) |
same | ENABLED, schedule */2 9-10 * * *. |
| P1.25 EXEMPT confirm | grep audit_log_action_check across migrations |
No CHECK migration on audit_log.action ever shipped; verbs are TypeScript-only via PW_ACTIONS + isCanonicalAction() per Clarification B. Keystone surfaces EXEMPT confirmed. |
Drift + hygiene captured
- P1.P15 NEW (tracked follow-up; NOT remediated): pw-api Cloud Run terraform-vs-gcloud drift surfaced by tonight's
terraform plan -target=google_cloud_scheduler_job.pw_api_positions_altruist_tick— pw-apigoogle_cloud_run_v2_service.pw_apishowedclient = "gcloud" -> null+ scaling block change. Likely from.github/workflows/deploy.ymlrunninggcloud run services update pw-api --image <sha>perpw-api/CLAUDE.md §Deploy pipeline. Same class as memoryfeedback_terraform_env_var_drift; reconcile in a focused follow-up PR. - Stale-branch sweep: ~100+
[gone]branches deleted across 6 repos (open-PR safe; active-worktree-protected). Branches in shared/.claude/worktrees/ checkouts skipped (will clean themselves up when those agent sessions next run). - All 6 local repos re-anchored to
origin/main.
Estate artifacts
| Repo | PR | Title |
|---|---|---|
| pw-api | #283 |
fix(positions-ingest): P0.K1 first-tick RLS-tenant-context throw + cursor-normalize defense-in-depth — MERGED + DEPLOYED |
| shared | (this PR) | docs(close-down): P0.K1 fix-deployed verification + Schwab dropped + P1.P15 infra-drift + memory + NEXT-PROMPT |
Memory updates
- NEW
feedback_cron_sweeper_force_rls_tenant_context.md— durable lesson: cron/sweeper querying a FORCE-RLS table MUST usecreateRequestClient(tenantId)to setapp.tenant_idbefore the query; rawpool.connect()throws''::uuidon RLS policy eval. Canonical pattern:snapshot-self-healing.ts:485. - UPDATED
project_p0k1_keystone_live_2026_05_26.md— RLS-fix-pending-verification status; Schwab dropped; dependent-not-adapter framing for tomorrow.
Doc-health audit (canonical docs current/stale)
| Doc | Last touched | Current/stale |
|---|---|---|
shared/ROADMAP.md |
2026-05-27 overnight | CURRENT (this close-out) |
shared/CHANGELOG.md |
2026-05-27 overnight | CURRENT (this entry) |
shared/strategy/CURRENT-STATE.md |
2026-05-27 overnight | CURRENT (this close-out re-anchor) |
shared/architecture/PWOS.md v1.7 |
2026-05-26 EOD | CURRENT (§4.1.1 keystone inventory accurate; clarification on Schwab-via-Altruist folded in this close-out where adapter section warranted) |
shared/architecture/decisions/ADR-positions-cost-basis-model.md Revision 3 |
2026-05-26 EOD | ACCEPTED, current — Decision 6 acquired_at CIO FYI accurate |
shared/architecture/decisions/ADR-custodian-data-ingest-orchestration.md Revision 2 |
2026-05-26 EOD | ACCEPTED, current — Decision 6 verb-name table reflects SHIPPED canonical 3-segment forms (fix-forward A from #395 landed) |
shared/runbooks/positions-ingest.md |
2026-05-26 EOD | CURRENT — §1 smoke-test gate is the load-bearing reading for operator re-smoke tomorrow |
shared/NEXT-PROMPT.md |
2026-05-27 overnight | CURRENT (this rewrite — deploy-confirm-gated re-smoke first) |
shared/architecture/BLUEPRINT.md |
2026-05-08 | STALE (tracked P1.P14; not refreshed tonight per dispatch direction) |
pw-infrastructure/CLAUDE.md |
2026-05-12 | STALE (tracked P1.P14; not refreshed tonight per dispatch direction) |
pw-api/CLAUDE.md |
2026-05-22 | STALE (no recent state-paragraph refresh; not flagged in scope tonight) |
pw-os-v2/CLAUDE.md |
2026-05-22 | STALE (no recent state-paragraph refresh; not flagged in scope tonight) |
pw-portal-v2/CLAUDE.md |
2026-05-22 | STALE (no recent state-paragraph refresh; not flagged in scope tonight) |
~/.claude/projects/-home-nick-projects-pw/memory/MEMORY.md |
2026-05-27 overnight | CURRENT (this index update) |
No doc still references governance.review_item.* (verified earlier via grep in PR #395 close-down). No doc references the inaccurate "0046 CHECK-widening" framing on the governance verb (PR #395 fixed all instances).
2026-05-26 (Monday EOD — P0.K1 keystone implementation LIVE; iteration-close)
The P0.K1 keystone branch shipped end-to-end this iteration — both gating ADRs ACCEPTED (schema + ingest orchestration), substrate migration LIVE in pwllc-prod, first-consumer Altruist ingest shipped, cross-repo PII canonical-add triplet maintained byte-equal across pw-api / pw-os-v2 / pw-portal-v2, canonical read contract typed-helper module landed, operator runbook published. 6 PRs across 3 repos total this iteration; substrate is READY to run, PENDING operator Cloud Scheduler terraform apply + smoke-test gate before the cron starts firing daily at 12:00 UTC.
6-PR keystone-build tally
| PR | Repo | Title | Scope |
|---|---|---|---|
#280 |
pw-api | Keystone substrate — migration 0058 + 12 audit verbs + PII_TAGS for 4 new tables | 4 tables (custodian_accounts + positions_snapshots + position_lots + position_dispositions scaffold) + 2 UNION VIEWs (tax_lots_unified + tax_dispositions_unified) + immutability trigger on positions_snapshots + 23 PII unit tests + 6 audit-verb unit tests; LIVE in pwllc-prod |
#397 |
pw-os-v2 | PII byte-equal port — 4 new tables | Cross-repo canonical-add triplet per pii-tags-cross-repo-sync-required-on-canonical-add memory |
#82 |
pw-portal-v2 | PII byte-equal port — 4 new tables | Same as above |
#281 |
pw-api | Altruist positions ingest — first consumer of substrate | 4 lib modules (content-hash.ts + cron-cursor.ts + retry-trail.ts + altruist-positions.ts + altruist-positions-tick.ts) + POST /v1/internal/cron/positions/altruist/run-next endpoint + 2 new altruist-client fetchers (fetchPositionsForAccount + fetchCostBasisForAccount) + 14 unit tests; all 7 operator-confirmed defaults from Open Q1–Q7 RESOLVED baked in |
#282 |
pw-api | positions-read.ts — canonical read contract for 11 P1-DEPENDENTS |
4 typed helpers (readPositionsForAccount + readOpenLotsForAccount + readAccountView + readClientPortfolio) per schema ADR Decision 5; computed-at-read totals (PnL never denormalized) |
#394 |
shared | runbooks/positions-ingest.md — operator runbook |
Altruist-only at v1 per Open Q7; smoke-test gate + failure-class triage flowchart + retry-budget reset + Decision 6 dashboard queries |
Migration 0058 verified read-only-safe against onchain tables (2026-05-26 EOD verification report): peer-table + UNION VIEW pattern confirmed; zero write statements against cost_basis_lots / cost_basis_dispositions / daily_snapshots / any other onchain table; immutability trigger attached to positions_snapshots only; position_lots is genuine peer (own CREATE TABLE; FK to custodian_accounts(id), NOT wallets). No fork-instead-of-peer risk.
Verb-name canonicalization (caught + fixed inline; fix-forward to ADR in this close-down)
The ingest ADR Decision 6 table originally listed three verbs in 2-segment shape that would have failed the canonical 3-segment regex enforced by parseAction at pw-api/src/lib/audit-actions.ts:728 + the 'all action values conform' assertion at pw-api/test/unit/audit-actions.spec.ts:12. Implementation correction landed the canonical 3-segment forms per the existing <table>.<singular>.<verb> precedent (clients.client.created / households.household.created):
| ADR DRAFT text | SHIPPED canonical 3-segment form |
|---|---|
custodian_accounts.synced |
custodian_accounts.account.synced |
position_lots.acquired |
positions.lot.acquired (groups under positions.* namespace alongside .snapshot.* + .ingest.*) |
position_lots.superseded |
positions.lot.superseded |
positions.ingest.retry.success / .retry.failure (dotted) |
positions.ingest.retry_success / .retry_failure (underscored; 3-segment) |
The ADR Decision 6 table is updated in this close-down PR to reflect the SHIPPED names; pw-api/src/lib/audit-actions.ts PW_ACTIONS registry is canonical for all consumers. Durable lesson codified to memory: ADR specs that enumerate audit-verb names should be verb-shape-validated against the canonical regex BEFORE promotion to ACCEPTED.
P0.K1 status
- Substrate: LIVE in pwllc-prod via pw-api
#280; migration 0058 dry-run + applied + verified. - Ingest code: shipped via pw-api
#281; READY to run but not yet running on cadence. - PENDING operator action P0.K1.next (NEW ROADMAP row): Cloud Scheduler
pwllc-prod-positions-altruist-tickterraform apply + 1-hour smoke-test gate perfeedback_scheduler_tick_smoke_testmemory. Runbook §1 enumerates the procedure. This is the gate between substrate-built and ingest-running. - All 11 P1-DEPENDENTS unblocked at substrate level: P1.D1 portfolio mgmt + P1.D3 client portal positions + P1.D4 IPS Maker + P1.D5 Investment Presentation Maker + P1.D6 cash-flow planning + P1.D7 performance reporting + P1.D8 rebalancing + P1.D9 cross-custodian tax reporting + P1.D10 EMF/PWAF CIO surface + P1.D11 chat-tool portfolio writes + P1.D2 Component 6 onboarding dashboard. All read via
pw-api/src/lib/positions-read.tstyped helpers per schema ADR Decision 5. - Next adapter on proven model: P0.K2 Schwab (file-based per 2026-05-17 gemini synthesis). Operator picks when to dispatch.
Deferred follow-ups captured as new ROADMAP rows
- P0.K1.next — Cloud Scheduler apply + smoke-test (operator action; the gate)
- P0.K1.test — keystone integration-test PR (cron-tick smoke + per-account-isolation e2e vs Altruist mock; Postgres-harness-bound)
- P0.K1.disposition — disposition consumer (sale-vs-transfer-vs-correction per deferred Decision 7)
- P1.P13 — PII egress canary HITL build (better detector + tokenizer + advisor disambiguation + per-span attestation; needs own ADR + Adam-CCO sign-off; supersedes earlier simpler "canary-block UX message" framing)
- P1.P14 — BLUEPRINT.md + pw-infrastructure/CLAUDE.md staleness refresh (tracked per 2026-05-23 doc-health audit)
Estate artifacts (this close-down PR)
| Repo | PR | Title |
|---|---|---|
| shared | (this PR) | docs(close-down): P0.K1 keystone iteration close — ROADMAP + ingest-ADR verb-name fix-forward + CURRENT-STATE + PWOS v1.7 + NEXT-PROMPT + runbook cross-refs |
Files touched
shared/ROADMAP.md— NEW §2.5 Recently SHIPPED; P0.K1 row marked DONE; 4 new follow-up rows (P0.K1.next + P0.K1.test + P0.K1.disposition); 2 new P1.P-platform rows (P1.P13 PII canary HITL + P1.P14 BLUEPRINT staleness tracker); F1 marked SHIPPED.shared/architecture/decisions/ADR-custodian-data-ingest-orchestration.md— Decision 6 table verb names canonicalized to SHIPPED forms (3 verbs + retry pair); 4 in-body verb references updated; naming-canonicalization callout added below the table.shared/CHANGELOG.md— this EOD entry.shared/strategy/CURRENT-STATE.md— verdict anchor re-anchored to 2026-05-26 EOD.shared/architecture/PWOS.md— bumped v1.6 → v1.7 with keystone substrate inventory addition.shared/NEXT-PROMPT.md— cold-start opener for 5/27 leads with operator Cloud Scheduler apply + smoke-test.
Coverage closed + opened
- P0.K1 keystone branch SHIPPED end-to-end — substrate LIVE; ingest READY; runbook published.
- 11 P1-DEPENDENTS UNBLOCKED at substrate level (consumer side is positions-read.ts; per-dependent UI/BFF work lands in separate P1.D* dispatches).
- Cross-repo PII canonical-add triplet discipline EXERCISED + GREEN across pw-api → pw-os-v2 → pw-portal-v2 within 30-60s window.
- Operator next action = Cloud Scheduler apply + smoke-test gate (P0.K1.next in ROADMAP).
2026-05-26 (Monday evening — ADR-custodian-data-ingest-orchestration PROMOTED DRAFT → ACCEPTED; 7 operator open questions resolved + 2 clarifications baked in + ROADMAP F2 fix-forward + schema ADR minor edits)
ADR-custodian-data-ingest-orchestration.md promoted DRAFT → ACCEPTED. All 7 operator open questions resolved with operator-accepted defaults; 2 substantive clarifications applied before promotion. P0.K1 keystone implementation PR now unblocked against both ACCEPTED ADRs (schema + ingest).
Seven open questions RESOLVED with operator-accepted defaults:
| # | Question | Resolution |
|---|---|---|
| 1 | Altruist cron cadence | 12:00 UTC daily (conservative; 7am ET) |
| 2 | Per-tick account bound | 50 accounts (3x over-provisioned at current ~15-account scale; load-bearing at 100+ clients) |
| 3 | Retry budget per account | 3 strikes (3 days at daily cadence) |
| 4 | First-sync mode | Manual operator-triggered at v1 (visibility during first-adapter rollout; auto-trigger considered for v2) |
| 5 | Cloud Scheduler region | us-central1 (matches pw-api Cloud Run region) |
| 6 | Per-tenant advisory-lock | Yes (409 on concurrent invocation) |
| 7 | Runbook scope at v1 | Altruist-only (extends per-adapter as each lands) |
Clarification A — disposition-detection scope (NEW Decision 7 added): ingest captures STATE only at v1. The prior position_lots.disposed audit verb (in Revision 1 Decision 6 verb taxonomy) is REMOVED — it belonged to the disposition consumer, not ingest. Sale-vs-transfer-vs-correction disambiguation is deferred to a separate later consumer (Altruist transactions ingest is the canonical disposition signal source via its explicit transaction_type='SELL' field per altruist.md §4.5). Rationale: a quantity-delta between snapshots can mean SALE / TRANSFER-OUT / CUSTODIAN CORRECTION — three vastly different tax treatments; auto-classification as a disposition risks wrong realized-gain reporting that lands on a client's tax return. The cost of false-positive disposition >> cost of deferred disposition surfacing. Decision 7 flags consequent open consequence for P1.D7 performance reporting (TWR/IRR cash-flow precision degrades on sales until disposition consumer lands). Verb count corrected 12 → 11.
Clarification B — mechanism precision (CHECK widening vs TypeScript-only) applied throughout the ingest ADR + the schema ADR + ROADMAP F2:
| Mechanism | When applied | What it requires |
|---|---|---|
| CHECK-widening migration | Adding a new custodian_accounts.source value (per schema ADR Decision 2) or a new review_items.kind enum value |
.sql migration following the migration-0046 pattern (DROP then ADD CONSTRAINT) |
| TypeScript-only registration | Adding a new audit_log.action verb (per ingest ADR Decision 6 + any future per-adapter or per-consumer verbs) |
.ts file edit in pw-api/src/lib/audit-actions.ts (PW_ACTIONS registry + isCanonicalAction() allowlist). audit_log.action is plain TEXT with NO CHECK to widen. |
The two mechanisms are now consistently distinguished throughout the docs; the prior conflations are fixed.
CIO FYI (NOT a gate; downstream reporting consequence): Altruist's aggregate open_date returned by /v2/accounts/{id}/cost-basis reflects the EARLIEST underlying lot's acquisition date (verified empirically against altruist.md §4.4 schema example). Synthetic-single-lots therefore carry acquired_at values that may pre-date the client's actual buy-in by years. Tax treatment: conservative + long-term-gain-favorable. Reporting + UX consequence: client positions on pwos.app/clients/$id + pwportal.app will display acquisition dates that look surprising for clients who joined PW recently ("why does this position show a 2019 date?"). P1.D1 + P1.D3 implementation PRs should render a tooltip / footnote on synthetic-aggregate positions. Jason-CIO already signed the related schema ADR Decision 4 + Open Question 2 aggregate calls earlier today (Revision 2 of schema ADR); this is the surfaced REPORTING effect of that limitation, not a new gate. Flagged in both ADRs (ingest ADR Decision 5 Altruist overlay + schema ADR Decision 6 §synthetic-single-lot).
ROADMAP F2 fix-forward (single-line edit; operator-flagged in the dispatch): the F2 entry previously read "migration (signing_tier column + audit verb add)" — implying audit-verb addition was a migration concern. Corrected to split: migration handles signing_tier column on review_items + partner_signoff admission to review_items kind CHECK enum (THIS is migration-0046 widening pattern; kind IS a CHECK enum); audit verb registration is TypeScript-only via PW_ACTIONS + isCanonicalAction() (audit_log.action is plain TEXT). Note added: canonical review-items audit verbs follow the review.item.* namespace pattern (per ADR-review-items-primitive.md line 123), NOT governance.review_item.* — preemptively codified though no instance of the wrong name exists in current shared/ docs (verified via grep).
Schema ADR ADR-positions-cost-basis-model.md minor edits (Revision 3; status stays ACCEPTED — NO re-promotion):
- Audit-verb registration mechanism corrected in Implementation-PR scope (was "via migration-0046 CHECK-widening pattern" → corrected to TypeScript-only per Clarification B).
position_lots.disposedverb removed from Implementation-PR scope verb list (per ingest ADR Decision 7; verb belongs to disposition consumer, not ingest).- CIO FYI callout added in Decision 6 §"synthetic single-lot pattern": Altruist aggregate
open_dateis the EARLIEST underlying lot's date — downstream REPORTING consequence flagged to Jason-CIO (already signed related calls; this is the surfaced reporting effect). - New Revision 3 entry added at bottom.
Estate artifacts
| Repo | PR | Title |
|---|---|---|
| shared | (this PR) | docs(adr): ADR-custodian-data-ingest-orchestration DRAFT → ACCEPTED; 7 operator answers + 2 clarifications + ROADMAP F2 fix-forward + schema ADR Revision 3 |
Files touched (4)
- EDITED
shared/architecture/decisions/ADR-custodian-data-ingest-orchestration.md— Revision 2 (DRAFT → ACCEPTED). All substantive edits: frontmatter status flip + gates ✅; Status section rewrite; NEW Decision 7 added (disposition-detection scope STATE-ONLY at v1); Decision 6 verb taxonomy revised (position_lots.disposedremoved; Clarification B precision note prepended); Adapter overlays prefaced with Clarification B mechanism-precision note; Decision 5 Altruist overlay gains CIO FYI callout foropen_datereporting consequence; Open Questions section converted from "Open" to "RESOLVED at ACCEPTED promotion"; Implementation-PR scope verb-count corrected 12 → 11 with Clarification B precision; Revision 2 entry added. - EDITED
shared/architecture/decisions/ADR-positions-cost-basis-model.md— Revision 3 (status stays ACCEPTED; minor edits only). Audit-verb registration mechanism corrected;position_lots.disposedremoved from scope list; CIO FYI callout added in Decision 6 §synthetic-single-lot; Revision 3 entry added. - EDITED
shared/ROADMAP.md— F2 entry edited: signing_tier column /partner_signoffadmission split from audit-verb registration; canonicalreview.item.*verb namespace pattern noted. - EDITED
CHANGELOG.md— this entry.
Coverage closed + opened
- P0.K1 keystone implementation PR: now UNBLOCKED against both ACCEPTED ADRs (schema + ingest). The implementation PR follows verbatim — schema is binding; operational contract is binding; 6 + 1 = 7 substrate decisions cover all design calls; no inline-decision re-debate at implementation time.
- P0.K2 Schwab + P0.K3 IBKR + manual: per-adapter overlay framework in place (Decision 5 + new Clarification B mechanism precision); each adapter's dispatch ships overlay-only PR scope.
- Disposition-detection consumer (Altruist transactions ingest + Schwab transactions file-ingest + cross-custodian transfer-detection): NEW dispatch surface opened — separately scoped from ingest per Decision 7. P1.D7 + P1.D9 dependents may need to coordinate with this consumer's timing.
- ROADMAP F2 conflation closed; canonical verb-namespace pattern preemptively codified.
- Schema ADR audit-verb registration mechanism: corrected to match the canonical TypeScript-only pattern; the prior CHECK-widening claim was a vestige of an earlier dispatch shape (per
feedback_verify_dispatch_schema_claims_before_authoring_migrationsmemory — the lesson applies).
2026-05-26 (Monday evening — ADR-custodian-data-ingest-orchestration DRAFT authored as F1-companion; gates P0.K1 implementation PR jointly with the schema ADR)
NEW shared/architecture/decisions/ADR-custodian-data-ingest-orchestration.md DRAFT authored as the F1-companion to the schema ADR (ADR-positions-cost-basis-model.md ACCEPTED earlier today). The two ADRs jointly govern the P0.K1 implementation PR: schema ADR locks the data shape, this ADR locks the operational contract — trigger/cadence/idempotency/partial-failure/backfill — across all keystone adapters (Altruist, Schwab, IBKR, manual, plus the existing onchain reference). Without this ADR the implementation PR would have decided the ingest contract implicitly inline, and P0.K2 Schwab + P0.K3 IBKR would have inherited whatever choices the first PR made.
Recommendation accepted (own ADR vs Decision 7 in keystone ADR): OWN ADR. Cross-cutting concern across N adapters (the schema is ONE decision; ingest is per-adapter overlay on a shared primitive). Lifecycle decoupled — schema rarely re-promoted; ingest evolves continuously as adapters land. Precedent: ADR-webhook-receiver-primitive.md (own ADR despite tight webhook-handling code coupling). F6 / P1.P10 KYC + RT crons can cross-reference the trigger primitive shape without inheriting keystone-substrate-specific concerns.
Six decisions surfaced explicitly:
- Decision 1 — Trigger + cadence: Cloud Scheduler
/v1/internal/cron/<adapter>/run-nextcursor-resumable primary (adopts the existingsnapshot-self-healingprimitive atpw-api/src/routes/internal.ts:281-284); webhook-augmented where source supports (Altruist NO; Quiltt YES via existing path); on-demand fallback for operator + first-sync. Per-adapter cadence: Altruist daily 12:00 UTC; Schwab daily 11:00 UTC (post-SFTP-drop); IBKR TBD per P0.K3; manual N/A; onchain (existing) stays 04:00 UTC. Reconciles with F6 — KYC/RT crons may adopt the same primitive shape but define their own per-surface work. - Decision 2 — Snapshot idempotency: content-hash dedup (SHA-256 over canonical-JSON of quantity + market_value + unit_price + currency_code + source_position_id; excludes provenance fields) + sentinel-row supersedes for corrections + natural-grain per
(custodian_account_id, instrument-key, as_of_date). Application-layer dedup discriminates "identical re-fetch" (skip silently, no audit) from "corrected re-fetch" (INSERT with supersedes_id + emit superseded audit row). Same pattern adapted for position_lots (synthetic-aggregate vs real-lot upgrade handled via supersedes chains). - Decision 3 — Partial-vs-fail-closed posture: PARTIAL COMMIT per-account-atomic. If Altruist returns 10 accounts and 2 fail, the 8 commit + the 2 emit
positions.ingest.account_failedaudit rows with classified error + retry counter. Per-account isolation; 3-strike retry budget before WARN escalation; sentinel-row retry trail perADR-gcs-worm-audit-mirrorPattern#2. 17a-4 substantiation: partial-commit IS the records-correct posture (failed records have audit-row evidence; successful records are immutably retained); all-or-nothing fail-closed would be WORSE (would abort 8 valid records on 1 transient failure). - Decision 4 — First-sync / backfill: current-day-only at v1; historical backfill DEFERRED to P0.K3. Altruist Open API surfaces "most recent positions" only (per altruist.md §4.3); Historical Data Feed (SFTP) is a separate provisioning flow. Schema trivially supports backfill (no constraint on
as_of_datedirection); backfill is additive when P0.K3 lands. - Decision 5 — Adapter overlays: per-source operational notes for Altruist (P0.K1 — sequential per-account fetch, 1 req/sec safety, OAuth refresh per existing pattern), Schwab (P0.K2 — file-batch SFTP polling, per-file cursor), IBKR (P0.K3 — TBD), manual (P0.K3 — operator-action only), onchain (existing — unchanged), Quiltt (existing webhook path — unchanged).
- Decision 6 — Audit + observability: 12 new canonical 3-segment audit verbs registered in
pw-api/src/lib/audit-actions.ts(custodian_accounts.synced,positions.snapshot.captured,positions.snapshot.superseded,position_lots.acquired/superseded/disposed,positions.ingest.account_failed/token_failed/list_failed/constraint_violation/batch_completed,positions.ingest.retry.success/failure). Skipped-duplicate events emit structured-log INFO only (no audit row; silent operation). Ops dashboard queries enumerated for "did every advisor sync today" + "how many accounts have pending failures" + per-tick latency p95.
Seven open questions flagged for operator confirmation before ACCEPTED promotion (all CTO-operational; NO CCO/CIO gates): cron cadence for Altruist (default 12:00 UTC); per-tick account bound (default 50); retry budget (default 3 strikes); first-sync mode (default manual operator-triggered vs auto on OAuth-callback); Cloud Scheduler region (default us-central1); per-tenant advisory-lock for concurrency (default yes); runbook scope at v1 (default Altruist-only).
Implementation-PR scope enumerated: new Cloud Scheduler job pwllc-prod-positions-altruist-tick (operator-direct terraform); new endpoint POST /v1/internal/cron/positions/altruist/run-next; new lib modules under pw-api/src/lib/positions-ingest/ (altruist-positions.ts + content-hash.ts + retry-trail.ts + cron-cursor.ts shared primitive); 12 new canonical audit verbs; new shared/runbooks/positions-ingest.md operator runbook; cron-tick smoke + per-account isolation + token-refresh-failed + content-hash dedup + sentinel-retry-trail tests.
Keystone schema ADR updated with cross-reference (status stays ACCEPTED — minor edit only):
- Frontmatter
referenceslist adds the new ingest ADR - Implementation-PR scope section notes the two ADRs jointly govern the implementation PR
Estate artifacts
| Repo | PR | Title |
|---|---|---|
| shared | (this PR) | docs(adr): ADR-custodian-data-ingest-orchestration DRAFT — F1-companion; gates P0.K1 implementation PR jointly with schema ADR |
Files touched
- NEW
shared/architecture/decisions/ADR-custodian-data-ingest-orchestration.md— DRAFT (~600 lines). 6 decisions; 7 open questions; implementation-PR scope; adapter taxonomy; per-source overlays; alternatives considered; references. - EDITED
shared/architecture/decisions/ADR-positions-cost-basis-model.md— minor: frontmatterreferencesadds the new ingest ADR + Implementation-PR scope section notes joint-governance. Status stays ACCEPTED (no re-promotion). - EDITED
CHANGELOG.md— this entry.
Coverage opened
- P0.K1 implementation PR — when the operator confirms the 7 open questions + flips this ADR to ACCEPTED, the implementation PR is unblocked against the joint schema + operational contract.
- P0.K2 Schwab + P0.K3 IBKR + manual adapters — inherit the shared contract; per-adapter overlays are thin; no per-adapter contract re-debate.
- F6 / P1.P10 Cloud Scheduler tick wire-up — KYC + RT crons can cross-reference the trigger primitive without inheriting keystone-substrate concerns. F6 stays independently scoped.
2026-05-26 (Monday evening — ADR-positions-cost-basis-model PROMOTED DRAFT → ACCEPTED per Jason-CIO sign-off)
ADR-positions-cost-basis-model.md promoted DRAFT → ACCEPTED per Jason-CIO sign-off on the three CIO-gating calls. The P0.K1 keystone implementation PR is now unblocked against the ACCEPTED schema. Substrate stays schema-binding without code-binding — implementation PR follows next iteration.
Three CIO answers baked in as substantive revisions:
Decision 4 reframed — FIFO-hardcoded across both tables → specific-lot identification (TradFi) + FIFO (onchain, preserved verbatim) + automatic average-cost (synthetic-single-lot). CIO directive: "ideally you can select which lots you are selling, but average works as well." The TradFi
position_lotsdisposition path is specific-lot-selectable (caller names the lot at write time); the onchaincost_basis_dispositionspath stays FIFO verbatim (shipped + Form 8949 tested; not touched); the synthetic-single-lot for aggregate-only sources (Decision 6) automatically yields average-cost (only one lot to consume from). The cross-custodiantax_lots_unifiedVIEW spans two methods; year-end Form 8949 default is per-source method (IRS-correct posture for crypto vs traditional brokerage as separate account types). Read-time method derivation from the consumed lot'sacquisition_sourceallows future override without schema change.Open Q2 RESOLVED, no change — CIO directive: "trust Altruist prices, all highly liquid tier-1 assets." The synthetic-single-lot pattern stands as drafted; Decision 6 commentary expanded to note the synthetic lot IS the average-cost case automatically (no special-case code path). Alternative B (separate aggregate field) rejection reinforced.
Open Q4 RESOLVED as trust-but-verify two-layer — positions_snapshots gains OPTIONAL
independent_unit_price_usd+independent_price_source+independent_priced_atcolumns. OPERATIONAL: custodian'smarket_value_usdIS the canonical statement value (no independent price gates the statement). AUDIT: independent fields are a backup verification capability for Adam-CCO's standing spot-check requirement; NOT operational gates. Same posture extends to vault NAVs later. Implementation PR ships the carrying schema + placeholder helper; first concrete spot-check consumer (operator-issued vs scheduled-canary) is a follow-up design call out of scope for P0.K1.
Remaining open questions (Q1, Q3, Q5, Q6, Q7) carried forward as CTO implementation calls or deferrals — NOT CIO gates; do NOT block ACCEPTED status nor the P0.K1 implementation PR.
Documentation impact (the ADR-internal substantive edits):
- Frontmatter:
status: DRAFT → ACCEPTED; gates annotated with ✅ for CTO operator-authority + Jason-CIO sign-off - Status section rewritten to enumerate the three CIO sign-offs in detail + remaining open questions
- Decision 3 schema: 3 new OPTIONAL columns on positions_snapshots
- Decision 4 substantially revised (peer tables + per-source method framing replaces FIFO-hardcoded framing); index names dropped
_fifosuffix (specific-lot UI uses same access pattern); position_dispositions commentary updated (disposition method derived at read time from lot's acquisition_source; no schema column added) - Decision 6 commentary expanded to highlight automatic average-cost behavior of synthetic-single-lot
- Open Questions reorganized: Q1, Q2, Q4 marked RESOLVED with CIO citation; Q3, Q5, Q6, Q7 carried forward
- Implementation-PR scope: replaces FIFO test with specific-lot + synthetic-single-lot degenerate average-cost + onchain FIFO pinning + independent unit-price spot-check tests; adds placeholder helper for independent-price recording
- Alternative B rationale strengthened with CIO confirmation citation
- Revision 2 entry added at bottom
Estate artifacts
| Repo | PR | Title |
|---|---|---|
| shared | (this PR) | docs(adr): ADR-positions-cost-basis-model DRAFT → ACCEPTED per Jason-CIO sign-off; specific-lot + onchain-FIFO + trust-but-verify pricing |
Files touched
- EDITED
shared/architecture/decisions/ADR-positions-cost-basis-model.md— Revision 2 (DRAFT → ACCEPTED). All substantive revisions above; ~140 lines net added (60 lines net edits + Revision 2 entry). - EDITED
CHANGELOG.md— this entry.
Coverage closed + opened
- All three CIO-gating calls satisfied: Decision 4 (lot accounting method); Open Q2 (synthetic single-lot fallback); Open Q4 (pricing snapshot policy).
- P0.K1 implementation PR unblocked against the ACCEPTED schema; can begin next iteration.
- Onchain Form 8949 paths preserved verbatim — no destructive migration of shipped
cost_basis_lots+cost_basis_dispositions; the new substrate is additive (peer tables) per Decision 4. - Trust-but-verify pattern codified as a substrate primitive — same pattern will extend to vault NAVs when those land.
2026-05-26 (Monday evening — ROADMAP F1 LANDED: ADR-positions-cost-basis-model.md DRAFT authored)
ROADMAP F1 (the gating ship for the P0.K1 keystone branch) LANDED as DRAFT ADR. New shared/architecture/decisions/ADR-positions-cost-basis-model.md is the binding architecture decision the keystone implementation PR follows next iteration. Schema-binding without code-binding: NO migration, NO implementation in this PR.
Six decisions surfaced explicitly (each with rejected alternatives):
- Decision 1 —
custodian_accountsfinalized column shape (canonical PW account identity table; multi-tenant + FORCE RLS + 7-year retention +pii_tagsJSONB; updates permitted — current-state row, append-only discipline lives onpositions_snapshotsnot here). - Decision 2 — Custodian-agnostic provenance via
source TEXT CHECKenum (altruist/quiltt/schwab_dailyfile/ibkr/onchain_wallet/manual) +source_account_id TEXT+UNIQUE(tenant_id, source, source_account_id). This is what makes Schwab a SECOND ADAPTER not a second schema (P0.K2 commitment). - Decision 3 —
positions_snapshotsfinalized column shape + as-of-date semantics: append-only point-in-time time-series; immutability trigger mirrorsaccount_balances(migration 0055); sentinel-row supersedes perADR-gcs-worm-audit-mirror.mdPattern#2for corrections; partial index excluding superseded rows covers the dominant "latest non-superseded per account per instrument as-of date" read. - Decision 4 — Lot accounting unification: peer tables (
position_lotspeer to existing onchaincost_basis_lotsfrom migration 0020, NOT a replacement) with read-side UNION via newtax_lots_unifiedVIEW. Preserves shipped onchain Form 8949 query paths verbatim; cross-custodian tax reporting (P1.D9) reads via the VIEW with consistent FIFO semantics. Folded-into-one-table alternative explicitly rejected for shipping-cost vs operational-aesthetics tradeoff. - Decision 5 — Canonical read contract for the 11 dependents (P1.D1–P1.D11): stored facts (quantity/market_value/unit_price/cost_basis_total) vs computed-at-read (unrealized PnL, account totals, cross-custodian roll-ups, concentration breakdown, TWR/IRR, realized PnL). Substrate stays simple; aggregates derived from immutable facts can't drift.
- Decision 6 — Altruist-ingest as first consumer + synthetic-single-lot pattern for lot-less sources. Altruist Open API exposes aggregate-level cost-basis only (per altruist.md §9 "Tax lot limitation"); ingest creates ONE
position_lotsrow per (account, instrument) withacquisition_source='aggregate'. Upgradable via sentinel-row supersedes when Altruist Realtime API or SFTP Data Feed surfaces real lots. Preserves unified FIFO + Form 8949 read path across sources.
Seven open questions flagged for operator + CIO sign-off before promotion to ACCEPTED: (1) lot unification scheme (CIO-valuation-methodology call); (2) synthetic single-lot vs separate aggregate field (CIO-tax-reporting call); (3) as-of-date correction discipline (CTO-recommended sentinel always); (4) pricing snapshot policy (CIO call on whether to capture independent market-data vendor unit_price alongside custodian's); (5) cash flow events for TradFi (widening migration 0021's cash_flow_events.source CHECK vs separate table); (6) manual position entry timing (CTO-recommended defer to P0.K3); (7) multi-currency (CTO-recommended defer).
Implementation-PR scope enumerated for the iteration that follows: migration (3 new tables + position_dispositions sidecar + 2 VIEWs + 4 new canonical audit verbs via 0046 CHECK-widening pattern); lib extensions (pii-tags.ts cross-repo byte-equal port to pw-os-v2 + pw-portal-v2; new positions-read.ts typed helpers; audit-actions.ts registration); ingest worker (altruist-sync.ts extension with new fetchPositionsForAccount + fetchCostBasisForAccount fetchers; synthetic-lot creation per Decision 6 mapping table); test coverage (FIFO consumption + ON CONFLICT idempotency + sentinel-supersedes correction + VIEW UNION shape pin).
Substrate posture: EXEMPT from P1.25 hard-gate per ROADMAP Decision 1 (reuses existing classified audit_log path). No CCO disclosure perimeter on substrate (perimeters land per-dependent at P1.D* gates). Adam-CCO not a gate on this ADR; Jason-CIO sign-off required on Decision 4 + Open Question 2 + Open Question 4 before promotion to ACCEPTED.
Estate artifacts
| Repo | PR | Title |
|---|---|---|
| shared | (this PR) | docs(adr): ADR-positions-cost-basis-model DRAFT — F1 / P0.K1 keystone gating ship |
Files touched
- NEW
shared/architecture/decisions/ADR-positions-cost-basis-model.md— DRAFT (~600 lines). 6 decisions; 7 open questions; implementation-PR scope; rejected alternatives; references all 7 cited migrations + altruist.md + 2 sibling ADRs. - EDITED
CHANGELOG.md— this entry.
Coverage opened
- P0.K1 implementation PR — unblocked (schema is binding); follows verbatim next iteration.
- All 11 P1-DEPENDENTS — read contract codified; substrate they consume will exist after the implementation PR ships.
- P0.K2 Schwab adapter + P0.K3 IBKR + manual — second/third-source adapters extend the
sourceCHECK enum via migration-0046 widening pattern; no new schema. Substrate proven by Altruist first-consumer ingest. - Cross-custodian Form 8949 export (P1.D9) —
tax_dispositions_unifiedVIEW shape committed.
2026-05-26 (Monday evening — reconciled roadmap PROMOTED to canonical shared/ROADMAP.md with 4 operator decisions baked in)
Reconciled all-inclusive roadmap promoted to canonical shared/ROADMAP.md with 4 operator-authority decisions applied as edits BEFORE promotion. The prior shared/strategy/2026-05-26-reconciled-roadmap-of-record.md DRAFT (shared #388) is now a redirect notice pointing to the canonical register. The /admin/approvals partner-signoff scope doc (shared #387) updated in the same PR to match Decision 2.
Decision 1 — P1.25 hard-gate clarification (Compliance Hub Phase D + classification automation): applies ONLY to surfaces requiring NEW classification (the substrate add-on itself); the existing classified audit_log path is EXEMPT. /admin/approvals partner.signoff.recorded, keystone positions_snapshots audit, and P1.A1 library/socials/content-drafts writeAuditLog additions all reuse the existing classified path — they do NOT block on P1.25.
Decision 2 — /admin/approvals signing-model reframe: passkey/OAuth signature_hash click + WORM audit_log + Google Workspace/Vault backup is the CANONICAL PRIMARY ceremony for ALL partner approvals (CCO, CISO, CIO). Anvil is reframed as an OPTIONAL formal-PDF-export wrapper for the narrow set of filed regulatory records where a countersigned PDF artifact is wanted (examiner expectation); same auth assurance, presentation-only. DocuSign is RETIRED — removed from the flow. ESIGN/UETA bootstrap C4.6 is no longer prerequisite for surface go-live; deferred until first PDF-export opt-in. CONSEQUENCE: the surface works DAY ONE on passkey + WORM + Vault with NO Anvil dependency.
Decision 3 — Planning-tier disclosure-language seed row: NEW PL1-disclosure seed row added to §3.7 covering all P2.PL calculator outputs (P2.PL1 Roth, P2.PL2 RMD, P2.PL3 Social-Security, P2.PL4 Medicare/IRMAA, P2.PL5 tax-ref dataset). Single batched CCO touch (NOT per-calculator); ships ahead of all four calculator surfaces.
Decision 4 — P2.PL5 versioned tax-ref dataset reordered AHEAD of P2.PL1: the versioned tax-ref foundation that all four planning calculators block on is reordered to ship FIRST. Can-start-now (no upstream dependency); unblocks P2.PL1-P2.PL4 sequentially.
Estate artifacts
| Repo | PR | Title |
|---|---|---|
| shared | (this PR) | docs(roadmap): promote reconciled roadmap → ROADMAP.md with 4 operator decisions; reframe #387 signing model |
Files touched
- NEW
shared/ROADMAP.md— canonical register written from scratch (~600 lines): §1 how to read; §2 ship this week (F1-F7); §3 dependency-ordered queue (P0-DEADLINE → P0-KEYSTONE → P0-GOVERNANCE → P1-PLATFORM → P1-DEPENDENTS → P2-PRODUCT → P3-EXPLORATION); §4 gated parked list; §5 reconciliation appendix; §6 cross-references; §7 what this doesn't decide. P1.25 carries EXEMPT clause (Decision 1); P0.G1-G4 reframed (Decision 2); §3.6a planning tier reordered (Decision 4); §3.7 includes NEW PL1-disclosure row (Decision 3). - REWRITTEN
shared/strategy/2026-05-26-reconciled-roadmap-of-record.md(#388) — replaced 442-line DRAFT with short redirection notice pointing to ROADMAP.md (drift foot-gun avoided). - EDITED
shared/strategy/2026-05-26-admin-approvals-partner-signoff-surface-scope.md(#387) — header status block, §1 intent, §2 constraint#2, §5 (canonical passkey + optional Anvil PDF wrapper), §6 (bootstrap downgraded to optional Phase 0c later-enabler), §10 (Q1 downgraded to "which records want PDF export — default NONE"), §11 (build size downgraded; passkey-only ships faster). - EDITED
shared/strategy/CURRENT-STATE.md— anchor refresh citing promotion + Decision 2 signing-model reframe.
Coverage closed + opened
- Roadmap dispersion (4 source scope/discovery docs + prior ROADMAP.md → one canonical register): CLOSED.
/admin/approvalssigning-model ambiguity (dual PATH 1/PATH 2 per-kind framing): RESOLVED in favor of passkey-canonical + Anvil-optional wrapper (Decision 2); surface ships day-one without ESIGN/UETA bootstrap.- P1.25 hard-gate scope creep (would have inappropriately blocked governance + keystone + library work on the substrate that doesn't apply to them): RESOLVED via EXEMPT clause (Decision 1).
- Planning-tier disclosure rework duplication (5 per-calculator CCO touches would have been wasteful): RESOLVED via single batched PL1-disclosure seed row (Decision 3).
- P2.PL1-P2.PL4 sequencing blocker (tax-ref dataset was downstream of first calculator): RESOLVED via reorder (Decision 4); tax-ref ships first.
Operator decisions formalized
All four decisions are CTO/CISO operator-authority (no CCO round-trip required for the decisions themselves; CCO touches remain only on the substantive seed rows enumerated in §3.7 of ROADMAP.md).
2026-05-26 (Monday afternoon — HIGH-2 conversation-lifecycle WORM mirror + M2 chat.message.superseded LIVE; ADR ACCEPTED)
Dedicated session for the HIGH-2 / ROADMAP P1.29 bundle deferred from the morning P0 sequence. 2 estate PRs landed + 1 shared/ doc PR closing the conversation-lifecycle WORM gap + the CCO-approved M2 chat.message.superseded immutable audit row. Compliance-bearing — closes the books-and-records gap for the advisor-chat surface's state-changing principal mutations (shared-session lifecycle + message supersession).
M2 implementation (pw-os-v2 #395) — new chat.message.superseded audit_log row at the edit/regenerate handler, written before supersedeMessagesFrom with fail-closed semantics (writeAuditLog throws → supersedeMessagesFrom never runs; route 5xxs). afterState carries conversation_id + source_message_id + superseded_count + per-message {message_id, role, content_sha256} array + replacement_content_sha256 + replacement_kind ('edit' | 'regenerate'). Content hashes only — never the full message text (PII stays in messages under existing 7-year retention; audit_log carries the tamper-evident reconciliation trail without duplicating bodies).
HIGH-2 conversation-lifecycle WORM mirror (same pw-os-v2 #395) — parallel writeAuditLog() calls land alongside the existing writeAiAudit() at the 3 sites in apps/api/src/routes/chat.ts (POST /share → conversation.shared_with; DELETE /share/:email → conversation.participant_revoked OR conversation.participant_left). Mirrors the 2026-05-23 dedicated decision-rationale audit row pattern (memory dedicated-decision-rationale-audit-row-pattern). Same posture as pre-existing writeAiAudit (post-state-change audit).
Canonical-registry alignment (pw-api #279) — CHAT_MESSAGE_SUPERSEDED: 'chat.message.superseded' registered in pw-api's canonical PW_ACTIONS taxonomy. The 3 conversation.* verbs are 2-segment and stay pw-os-v2-only (pw-api's strict 3-segment PW_ACTIONS invariant doesn't admit them; pw-api never emits them either — asymmetry is benign).
ADR promotion (DRAFT → ACCEPTED) — shared/architecture/decisions/ADR-chat-message-superseded-audit-row.md promoted 2026-05-26 with as-shipped section. Both promotion criteria satisfied (CTO/CISO as-shipped review via pw-os-v2 #395 + pw-api #279; CCO Reg S-P / Rule 17a-4 / 204-2 review satisfied 2026-05-20). Bundled conversation-lifecycle scope covered under the same CCO authorization umbrella per memory cco-gate-scope-substrate-vs-client-facing-2026-05-19.
Two dispatch-vs-code-shape divergences caught + resolved in flight via AskUserQuestion:
- The dispatch said "widen
audit_log_action_checkCHECK constraint" — verified thataudit_log.actionhas NO CHECK constraint in either repo (the migration-0046 CHECK-widening pattern lives on pw-os-v2'sai_audit_log, notaudit_log). No migration shipped in this bundle; adding a verb is TypeScript-only. - The dispatch said add 4 verbs to pw-api's
PW_ACTIONS— verified that 3 are 2-segment (violates pw-api's strict 3-segment invariant). Operator confirmed Option 1 (add onlyCHAT_MESSAGE_SUPERSEDEDto pw-api PW_ACTIONS; conversation.* verbs stay pw-os-v2-only).
Estate PRs (2 + 1 shared/)
| Repo | PR | Title |
|---|---|---|
| pw-api | #279 |
feat(audit-actions): register chat.message.superseded canonical verb (HIGH-2 / M2) |
| pw-os-v2 | #395 |
feat(audit-log): wire conversation lifecycle + chat.message.superseded WORM rows (HIGH-2 / M2) |
| shared | (this entry) | docs: HIGH-2 / M2 conversation-lifecycle + chat.message.superseded LIVE — ADR ACCEPTED + COMPLIANCE-CHANGELOG + ROADMAP P1.29 closed |
Coverage closed
- HIGH-2 (conversation-lifecycle WORM gap surfaced 2026-05-25 evening diagnostic) — CLOSED.
- M2 (
chat.message.superseded, ADR DRAFT since 2026-05-20) — CLOSED + ADR promoted DRAFT → ACCEPTED. - ROADMAP P1.29 (deferred to dedicated session per
overnight-2026-05-25/CLOSING-SESSION-RECOMMENDATIONS.md) — moved to Recently SHIPPED at P1. - ROADMAP P1.21 M2 sub-item (bundled into P1.29 per dispatch) — closed.
Verification
- pw-api
#279: 1664/1664 unit tests pass; tsc + lint clean; CI all-green (Lint+typecheck+test + Migration dry-run + Gitleaks + Trivy + npm audit) — merged 15:08Z. - pw-os-v2
#395: 1439/1439 unit tests pass; typecheck + lint clean; auto-merge pending CI. - Post-deploy smoke: operator can verify by triggering a self-share at pwos.app/chat → Share button, then querying audit_log for
action='conversation.shared_with'at /admin/compliance/audit-log.
2026-05-26 (Monday — iteration close: P0 substrate fully landed + GitHub abuse-heuristic suspension incident + resolution)
Morning P0 + doc-refresh + 3 audience-segmented briefs + cross-repo PII_TAGS triplet ALL LANDED — 4 morning compliance findings closed (HIGH-1 + MEDIUM-1 + MEDIUM-2; HIGH-2 deferred to dedicated session per CLOSING-SESSION-RECOMMENDATIONS.md as M effort). 7 estate-wide PRs merged + 4 supporting shared/ PRs across pw-api + pw-os-v2 + pw-portal-v2 + pw-infrastructure + shared/. The 2026-05-25 overnight unattended cleanup run's 21 deliverables fully consumed.
Suspension incident at ~12:18Z + resolution. During the rapid P0 push-burst (5 PR opens + auto-merge enables across 3 repos in ~2 minutes), all PR-triggered CI started failing on actions/checkout@v6 with remote: Your account is suspended + HTTP 403. Root cause was a transient GitHub abuse-heuristic restriction — per-token throttle, not an account-level suspension. Resolved without GitHub Support escalation via: (a) fresh pull_request trigger on each blocked PR (close/reopen — operator-side close/reopen cancels auto-merge so each had to be re-merged manually after the fresh CI run); (b) credentials rotated to SSH + refreshed gh OAuth under single identity rivendale; (c) PW_POSTURE_PROBE_PAT re-minted (older PAT was implicated in the abuse-heuristic burst); (d) GH_PAT_PW_API survived the rotation (lives as the pw-infrastructure CI cross-repo pw-api typecheck fetch surface — must NOT be revoked without re-mint per the new pw-infra-workflow-pats-active-dont-sweep memory). 3 new memory artifacts codified the prevention + recovery + workflow-PAT-inventory discipline; a CLAUDE.md §1.4 addendum proposal staged at shared/ideas/2026-05-26-claude-md-1-4-addendum-throttle-and-fresh-trigger.md for operator review (NOT auto-landed — structural change per existing §1.4 gate).
HIGH-1 closure end-to-end — pw-api #278 (canonical-add for 5 intern_* tables) + pw-os-v2 #394 + pw-portal-v2 #81 (byte-equal mirrors of pw-api canonical post-#278-merge, drift gate validated at sha cdf54607…); the per-repo apps/api/scripts/check-pii-tags-drift.mjs gate validated each mirror against the post-#278 pw-api/main canonical and passed clean. Closes ROADMAP P1.28.
MEDIUM-1 closure — pw-infrastructure #204 extends .gitignore glob *.tfplan → *.plan covering text-form plan captures (caught pre-incident; the 102KB cloud-run-baseline/docs.plan + 19KB service-accounts/sa.plan files were untracked at risk of git add -A capture per the 83f346b incident class). Closes ROADMAP P1.30.
MEDIUM-2 closure — shared #377 prepended 2 Rule 204-2 audit-trail entries to compliance/COMPLIANCE-CHANGELOG.md: 2026-05-22 CES Implementation Phase 1 substrate merged (inert against empty table) + 2026-05-23 Doc-pipeline Slice 1+2 V1 LIVE + PRODUCTION-VERIFIED (5-control CCO-perimeter ratified). Closes ROADMAP P1.31.
Doc-refresh PR (shared #378) landed 6 B?-DRAFTs + 3 architecture refresh files in one PR: CHANGELOG 2026-05-26 entry + CURRENT-STATE 2026-05-26 anchor (Z-EVALUATION 5-edit micro-patch, NOT full-section rewrite — preserved ~250 lines of cascade content) + ROADMAP P1.28/P1.29/P1.30/P1.31 added + deadline countdown (8 days) + BlockSkunk Phase 0 status check + README Firm State header bump + NEXT-PROMPT.md in ASCII layout + pw-learnai added to Related repos + CLAUDE.md §6 + §6.1 + §1.4 + §2.0.1 + §8.1 patches + Last-Verified header bump + BLUEPRINT.md drop-in replace (refreshed to 2026-05-26 state with new §6 in-perimeter doc-conversion sidecars + cross-reference replacing stale PW-STATE-2026-05-11.md with shared/architecture/PW-STATE.md) + PW-STATE.md NEW canonical universal-team brief (14 sections; readable in 15 minutes) + PWOS.md v1.5 → v1.6 (new §8.10 nighttime diagnostic findings + GitHub Actions suspension narrative + new §19 full-LLM-context handoff for fresh Claude instances).
Audience-segmented dated briefs (shared #379) landed PW-PUBLIC-2026-05-26.md (Marketing-Rule §206(4)-1-safe) + PW-INTERNAL-2026-05-26.md (interns + contractors + prospective hires under NDA) to strategy/. PW-FOUNDERS-2026-05-26.md staged on disk for operator-commit-only path at shared/governance/ per CLAUDE.md §1.3.
NEXT-PROMPT.md refreshed (shared #376) with the 5/26 AM cold-start prompt referencing the overnight's 21 deliverables; the iteration close ritual will overwrite again at end of next iteration for the 5/27 AM session.
Estate-wide PRs (7 + 4 shared/)
| Repo | PR | Title |
|---|---|---|
| shared | #376 |
docs(next-prompt): refresh for 5/26 AM — overnight run completed |
| shared | #377 |
compliance(changelog): 2026-05-22 CES Phase 1 + 2026-05-23 doc-pipeline Slice 1+2 LIVE — Rule 204-2 audit-trail entries |
| shared | #378 |
docs: 2026-05-26 morning doc-refresh — CHANGELOG + CURRENT-STATE + ROADMAP + README + CLAUDE + BLUEPRINT + PW-STATE + PWOS v1.6 |
| shared | #379 |
docs(audiences): 2026-05-26 dated briefs — PUBLIC + INTERNAL snapshots |
| pw-api | #278 |
feat(pii-tags): canonical-add for 5 intern_* tables (HIGH-1) |
| pw-os-v2 | #394 |
feat(pii-tags): byte-equal mirror of intern_* canonical blocks (HIGH-1) |
| pw-portal-v2 | #81 |
feat(pii-tags): byte-equal mirror of intern_* canonical blocks (HIGH-1) |
| pw-infrastructure | #204 |
chore(gitignore): extend *.tfplan → *.plan glob (MEDIUM-1 pre-incident) |
| shared | (this entry) | docs(state): 2026-05-26 iteration close — P0 complete + suspension incident + resolution |
Memory artifacts (3 new)
| Artifact | Purpose |
|---|---|
github-actions-suspended-403-fresh-trigger-not-rerun |
Fresh-trigger via close/reopen on a CI "account suspended" 403, never gh run rerun |
throttle-automated-git-ops-to-avoid-abuse-heuristic |
Pace multi-PR push-bursts (30-60s between opens + auto-merge enables) to avoid the abuse heuristic |
pw-infra-workflow-pats-active-dont-sweep |
GH_PAT_PW_API + PW_POSTURE_PROBE_PAT inventory — re-mint, don't sweep during rotation |
Proposed (NOT auto-landed — operator review)
| Path | Purpose |
|---|---|
shared/ideas/2026-05-26-claude-md-1-4-addendum-throttle-and-fresh-trigger.md |
CLAUDE.md §1.4 addendum DRAFT codifying the 3 memory artifacts as load-bearing agent guidance |
2026-05-26 (Monday morning — overnight cleanup run completed; doc-refresh PR landing 6 B?-DRAFTs + 3 architecture refresh files)
The 2026-05-25 overnight unattended cleanup run completed; the morning operator lands the produced drafts as a single doc-refresh PR plus 3 P0 substrate PRs for the HIGH-1 PII_TAGS gap + MEDIUM-1 .gitignore glob + MEDIUM-2 COMPLIANCE-CHANGELOG drift. Production was confirmed GREEN throughout the overnight (no incidents, 7 Cloud Run services Ready on 2026-05-23 revisions, 9 scheduler jobs firing, GCS WORM lock re-verified True / 220903200s 7yr). The overnight produced 13 drafts (plus 3 architecture refresh + 3 audience-segmented dated docs added in close-pass = 21 total deliverables at /home/nick/projects/pw/overnight-2026-05-25/) totaling ~392KB; zero git mutations across all 9 estate repos. Three corrections landed via the overnight diagnostic vs the 5/25 evening anchor: (a) DOC_CONVERTER_GOTENBERG_URL + DOC_CONVERTER_DOCLING_URL env vars confirmed LIVE on pw-os-00346-jk4 (corrects the stale memory doc-pipeline-slice-1-live listing wire-up as a pending operator follow-up); (b) actual open dependabot PR count is 22, not 8 (the 5/25 evening anchor pre-dated nexus-core's 10 OSS pip PRs opened the same evening); (c) GCS WORM lock re-verify CONFIRMED in force (carry-forward closes). Two carry-forwards close: GCS WORM re-verify (carry P3.19) + P1.27 governance/review-items POST creation route (shipped 2026-05-23 evening via pw-api #276).
Morning P0 substrate sequence (in flight at time of this entry):
- PR 1 — shared/ NEXT-PROMPT.md refresh for the 5/26 AM cold-start prompt (overwrote the 5/25 evening stale version that still referenced the overnight as queued/if-the-run-completed).
- PR 2a — pw-api PII_TAGS canonical-add for the 5
intern_*tables (HIGH-1; closes thegrep "intern_" pw-api/src/lib/pii-tags.ts = 0 matchesgap from the overnight verification table). Same-day byte-equal mirror to pw-os-v2 + pw-portal-v2 ships in immediate follow-on PRs per memorypii-tags-cross-repo-sync-required-on-canonical-addto keep the drift gate green for downstream consumer PRs. ROADMAP P1.28. - *PR 2b — pw-infrastructure .gitignore .plan glob fix (MEDIUM-1, pre-incident — caught before the next
git add -Aaccident; ROADMAP P1.30). - PR 2c — shared/ COMPLIANCE-CHANGELOG entries for 2026-05-22 CES Phase 1 + 2026-05-23 doc-pipeline Slice 1+2 LIVE — Rule 204-2 audit-trail surface restored (MEDIUM-2; ROADMAP P1.31).
- PR 3 — shared/ doc-refresh (this PR) landing the overnight's 6 doc drafts (B1 CHANGELOG / B2 CURRENT-STATE / B3 ROADMAP / B4 README / B5 CLAUDE.md / B6 NEXT-PROMPT already done in PR 1) plus 3 architecture refresh drafts (H1 BLUEPRINT drop-in / H2 PW-STATE.md new file / H3 PWOS.md v1.5 → v1.6 patches).
- PR 4 — shared/ audience-segmented dated docs (H4 PW-PUBLIC + H5 PW-INTERNAL to
shared/strategy/; H6 PW-FOUNDERS staged for operator commit toshared/governance/per CLAUDE.md §1.3 operator-commit-only path).
HIGH-2 (conversation-lifecycle WORM mirror with M2 bundle) stays deferred to a dedicated session per CLOSING-SESSION-RECOMMENDATIONS.md — M effort (3-4 hours wall-clock); not crammed into the morning sequence.
Operational follow-ups carried to next iteration (per overnight P2-P4): 22-PR dependabot sweep (15 CLEAN bulk auto-merge candidates: pw-api #277 + pwos-core #41/#42 + nexus-core #34-#43; 5 manual triage including pw-portal-v2 majors; 3 pw-website Tests-class single-root-cause investigation; 2 stale BEHIND @dependabot recreate); shared/ archival sweep (31 archive-candidates from A-CLEANUP-PLAN.md); _reference/ cleanup (5 malformed symlinks); Cloud Scheduler tick wire-up gap for KYC + risk-tolerance crons (confirm intended trigger); pwportal.app design-system v2.0 migration (ROADMAP P2.17).
Reg S-P / Reg XP June 3, 2026 deadline countdown: 8 days. The 3 CCO DocuSign signatures + vendor 72-hour clause closure remain CCO-led.
Estate-wide PRs (morning sequence)
| Repo | PR | Title |
|---|---|---|
| shared | #376 |
docs(next-prompt): refresh for 5/26 AM — overnight run completed |
| shared | #377 |
compliance(changelog): 2026-05-22 CES Phase 1 + 2026-05-23 doc-pipeline Slice 1+2 LIVE — Rule 204-2 audit-trail entries |
| shared | (this PR) | docs: 2026-05-26 morning doc-refresh — CHANGELOG + CURRENT-STATE + ROADMAP + README + CLAUDE + BLUEPRINT + PW-STATE + PWOS v1.6 |
| pw-api | #278 |
feat(pii-tags): canonical-add for 5 intern_* tables (HIGH-1) |
| pw-infrastructure | #204 |
chore(gitignore): extend *.tfplan → *.plan glob (MEDIUM-1 pre-incident) |
(pw-os-v2 + pw-portal-v2 PII_TAGS byte-equal mirror PRs queued to push immediately after pw-api #278 lands; numbers not yet allocated.)
2026-05-25 (Sunday evening — nighttime diagnostic + overnight cleanup queued)
Pre-resume nighttime diagnostic returned production GREEN + 2 HIGH compliance findings for 5/26 morning P0 sequencing. Four parallel read-only agents ran a comprehensive estate check after the 2026-05-23 ship train. Production health = GREEN: 7 Cloud Run services Ready on latest 2026-05-23 revisions (pw-api 00265-vql, pw-os 00346-jk4 with 3600s timeout, pw-portal 00088-8c7, gotenberg + docling sidecars internal ingress, pw-api-webhooks, langfuse); 9 Cloud Scheduler jobs firing on cadence (annual ADV cron OK 5/25 03:30Z); 1 benign error in 48h (/.env bot probe on langfuse); Stream Z zero activations; DOC_CONVERTER_GOTENBERG_URL + DOC_CONVERTER_DOCLING_URL env vars LIVE on pw-os-00346-jk4 (corrects the stale MEMORY entry doc-pipeline-slice-1-live that listed env-var wire-up as a pending operator follow-up — it shipped on the 2026-05-23 20:44Z revision). Code health = READY-TO-RESUME with light cleanup: all 5 runtime repos clean on main; 8 dependabot PRs open (pw-api #277 clean ready-to-land; pw-portal-v2 #78/#79/#80 three opened 5/25 incl. quiltt v5→v6 + react-markdown v9→v10 majors; pw-website #108/#110/#121 fail tests as a single root-cause class — vitest 4 / lucide major / plugin-react v6). UX = public surfaces healthy (the WSL diagnostic box has no Chrome cookie store so 6 authenticated pwos.app routes — /admin/interns, /admin/altruist, /clients, /clients/import, /chat mode selector, advisor markdown — gated to /auth-required as expected; auth-walls work correctly; operator confirmed /admin/interns end-to-end with intern GitHub names added). 3 UX nits surfaced: pwos.app/login returns 404 (root IS the login surface); cold pre-hydration white-flash; pwportal.app still on superseded v1.0 warm-gold brand while pwos.app is on canonical v2.0 blue-centric per docs/firm/design-system.md §11. Security/compliance returned 2 HIGH + 2 MEDIUM:
HIGH-1 — PII_TAGS interns canonical-add gap. pw-api/src/lib/pii-tags.ts has zero entries for the 5 new intern_* tables (engagement / milestone / status_update / evaluation / document) despite the substrate carrying intern_engagement.student_name (TEXT) + intern_evaluation.scores/justification (free-text JSONB) + intern_status_update.shipped/blockers (free TEXT) + *_by_email columns. The writeAuditLog redaction scanner still catches content-pattern PII in audit snapshots but the structural per-table tag is load-bearing for excludeHighPii exports + the partner-facing security posture claim. Per memory pii-tags-cross-repo-sync-required-on-canonical-add — adding to canonical needs same-day byte-equal mirror to pw-os-v2 + pw-portal-v2 or the next consumer PR trips the drift gate. Triplet PR queued as morning P0.
HIGH-2 — shared-chat lifecycle WORM gap. New verbs conversation.shared_with / conversation.participant_revoked / conversation.participant_left (pw-os-v2 #384, LIVE 2026-05-22) write only to ai_audit_log via writeAiAudit — non-WORM, non-immutable. Sharing a chat conversation grants RBAC access to another partner; that's a state-changing principal mutation that should land an immutable audit_log row per SEC 17a-4/204-2. Same class as the 2026-05-20 M2 finding (chat.message.superseded, CCO-approved but not shipped). Bundle the conversation-lifecycle WORM emissions with M2 implementation per the dedicated decision-rationale audit row pattern from 2026-05-23 (memory dedicated-decision-rationale-audit-row-pattern).
*MEDIUM-1 — .gitignore .plan glob. pw-infrastructure/.gitignore:14 catches *.tfplan but the local working tree carries terraform/cloud-run-baseline/docs.plan (102KB) + terraform/service-accounts/sa.plan (19KB) — at risk of accidental git add -A capture per the 83f346b incident class. Caught pre-incident. Extend glob to *.plan + delete local files.
MEDIUM-2 — COMPLIANCE-CHANGELOG drift. compliance/COMPLIANCE-CHANGELOG.md ends 2026-05-21. Doc-pipeline Slice 1+2 LIVE deploy 2026-05-23 was CCO-perimeter (5-control guarantee per memory doc-pipeline-slice-1-live). Rule 204-2 audit-trail-of-the-audit-trail entry missing.
Overnight cleanup prompt authored at strategy/2026-05-25-overnight-cleanup-prompt.md — a 4-6h unattended Claude Code session covering: shared/ archival census (strategy/ at 2.3M / 63 entries; diagnostics/ 14 files; dispatches/ 7 dated dispatches), 6 canonical doc drafts (CHANGELOG / CURRENT-STATE / ROADMAP / README / CLAUDE.md / NEXT-PROMPT), 9-repo health pass (active + 3 open-source: pwos-core / nexus-core / pw-learnai), _reference/ disposition (10 deprecated repos), full GCP infra inventory with drift-vs-IaC, MEMORY.md trim plan (currently >24.4KB over the partial-load limit), COMPLIANCE-CHANGELOG drift survey. Morning operator reviews drafts + lands as single doc-refresh PR. NEXT-PROMPT.md authored at shared/NEXT-PROMPT.md for predictable 5/26 wake-up retrieval with P0/P1/P2 sequencing baked in. New finding from the Cloud Run probe: KYC + risk-tolerance cadence crons are absent from Cloud Scheduler — confirm intended trigger mechanism on 5/26.
Estate-wide PRs (1, shared)
| Repo | PR | Title |
|---|---|---|
| shared | (this PR) | docs(state): 2026-05-25 nighttime diagnostic + overnight cleanup prompt + 5/26 NEXT-PROMPT |
2026-05-23 (Iteration close — post-Slice-2 onboarding-routes + xlsx/pptx + PWOS.md v1.5)
Four PRs landed across three repos in one parallel-subagent iteration; both pw-os-v2 BFF onboarding 502 gaps closed; doc-pipeline extended to xlsx + pptx via same Gotenberg path; canonical firm-architecture doc refreshed from v1.4 to v1.5 capturing 5 days of post-2026-05-19 shipments.
Day organized around four threads (each its own subagent in parallel):
P1.18 — pw-api advisor HTTP routes for KYC (PR #274). Closes the pw-os-v2 BFF
advisor-kyc.ts502 gap that's been latent since the Component 2 KYC substrate landed 2026-05-18 without its advisor route layer. 3 routes:GET /v1/internal/kyc/sessions(cross-client list withclient_id/state/advisor_action/ cursor filters;advisor_action='pending'expands to in-flight state set;advisor_actionwins overstatewhen both supplied);GET /v1/internal/kyc/clients/:clientId/sessions(per-client timelinecreated_at DESC, limit 500);POST /v1/internal/kyc/sessions/:sessionId/decision(body{decision: approve|escalate|reject, decision_reason: string (req, max 2000)}; CCO + CFP only; mapsapprove→approved,reject→rejected,escalate→escalated). New substrate audit verbkyc.session.escalatedadmitted via the 0046 CHECK-widening pattern — theescalatedstate had no substrate-action mapping before (vendor-driven flow never lands directly there); the new map entry +LIFECYCLE_ACTION_FOR_STATEentry give advisor-driven escalation a substrate row + the Tier-2 boundary parity with vendor-drivenoptional_human_review. Dedicated advisor-decision audit row pattern — capturesdecision_reasonin a separateaudit_logrow alongside the substrate + lifecycle rows fromtransitionKycSession, so SEC-exam queries byaction='kyc.session.approved'surface both the state-machine row + the operator rationale row. PII projection: read routes omitverdict_json+flags_json+vendor_session_id(pii.high per Pick 3 2026-05-19); an unmask path mirroringsigned-documents/reveal-signer-emailis a separate PR. 1591/1591 tests pass (+29); typecheck + lint clean; auto-merged on green CI.P1.19 — pw-api advisor HTTP routes for risk-tolerance (PR #275). Sibling closing the
advisor-risk-profile.ts502 gap. 3 routes mirroring P1.18: cross-client list (with bucket filter viaCOALESCE(advisor_override_bucket, bucket)for effective-bucket matching; limit 1..200); per-client timeline (capped 500); decision (body{decision, decision_reason, override_bucket?}; 4 verbsapprove|escalate|reject|bucket_override; role-gated to CCO + CFP + CIO — CIO admitted vs KYC's CCO+CFP set per Pick 4b). Thebucket_overridepath routes throughapplyAdvisorOverridelib (cross-bucket CCO-cosign gate; CCO actor satisfies the gate themselves) rather thantransitionRiskToleranceSession. 4 new substrate audit verbs (risk_tolerance.session.approved/rejected/needs_review+ lifecycleonboarding.risk_tolerance.rejected) — the risk-tolerance substrate map was originally sparser than KYC's (onlyinitiated/responses_submitted/scoredcovered); these give the advisor decision endpoint full substrate + lifecycle coverage. Note: risk-tolerance has noescalatedstate in migration 0051 CHECK constraint (distinct from KYC); advisor-driven escalation landsneeds_review. 1626/1626 tests pass (+35); typecheck + lint clean; auto-merged on green CI. Carried-forward gap: samePOST /v1/internal/governance/review-itemscreation route absence as P1.18 — BFFs degrade gracefully viareview_item_erroron both escalate + cross-bucket override paths.Doc-pipeline Slice 2 follow-on — xlsx + pptx MIME support (pw-os-v2 #392). Single-PR extension of the Slice 2 substrate via the same Gotenberg
/forms/libreoffice/convertpath.apps/api/src/lib/doc-converters.tsintroduces anOFFICE_MIMESset (DOCX + XLSX + PPTX);attachments.tsSUPPORTED_MIMES+convertInboundDocs(inbound, classification)routing extended; xlsx + pptx route to Gotenberg for BOTH client-data and b2b-counterparty (binary-extract via Docling isn't semantically useful for spreadsheets/presentations — Gotenberg's PDF output is what advisors want);apps/web/src/components/advisor/ChatPage.tsxSUPPORTED_MIMES+<input accept=...>extended. 8 new tests; no new audit verbs (reuseschat.attachment.converted/conversion_failed). Watched the security-grep gotcha (memorydoc-pipeline-slice-1-livelesson#1—Bearer [a-zA-Z0-9]byte pattern in test fixtures); avoided cleanly. Auto-merged on green CI.Architecture refresh — PWOS.md v1.4 → v1.5 (shared #371). Canonical firm-architecture doc was 5 days stale; refreshed to capture 11 substantive shipment threads (substrate-audit remediation; substrate decisions + capability inventory; CES design ACCEPTED + Phase 1; chat model policy; P0 timeout fix; P1 adaptive request mode; pwportal chat UX; Altruist OAuth restore; Wealthbox client-import route + 3 pwos.app evening ships; interns module end-to-end; doc-pipeline Slice 1 + Slice 2 V1; 204-2 Classification Matrix; hygiene train). +57/-7 lines (1312 → 1362). New §8.9 captures per-surface model routing + max_tokens policy + adaptive planner; new §11.10 captures 204-2 frame correction + Phase-1 classification matrix + CES design + canonical 5-control CCO-perimeter pattern; §4.1 expanded to "Seven active services" with the two doc-converter sidecars; §9.4 + §15 cross-reference updates. Style compliance — no personal names in new content (caught + fixed one "Adam-CCO" → "the CCO" reference during authoring); existing names in §2 + §18 (substantive-obligation identification) preserved per the two-tier provenance framework.
New ROADMAP item surfaced: P1.27 — POST /v1/internal/governance/review-items creation route is missing in pw-api. The BFFs degrade gracefully via review_item_error, but the failed-KYC HITL Tier 2 escalation + risk-tolerance cross-bucket override flows never create the governance review_item needed for CCO-cosign UI surfacing. Substrate exists (state machine + GET routes + vote/action routes); only the CREATION route is missing. S effort; mirrors the KYC/risk-tolerance 3-route pattern minus the timeline route.
Reusable substrate pattern codified: dedicated decision-rationale audit row. When an advisor decision endpoint takes a decision_reason, write a DEDICATED audit_log row capturing the rationale alongside the substrate + lifecycle rows that transitionFooSession already writes. SEC-exam queries by action='kyc.session.approved' (substrate) + action='kyc.session.advisor_decision' (rationale) get both. Shipped in #274 + #275 across KYC + risk-tolerance.
Estate-wide PRs (4 — 1 shared + 2 pw-api + 1 pw-os-v2)
| Repo | PR | Title |
|---|---|---|
| shared | #371 |
docs(architecture): PWOS.md v1.5 refresh — 5 days of post-v1.4 shipments |
| pw-api | #274 |
feat(kyc): wire /v1/internal/kyc/* advisor HTTP routes (P1.18) |
| pw-api | #275 |
feat(risk-tolerance): wire /v1/internal/risk-tolerance/* advisor HTTP routes (P1.19) |
| pw-os-v2 | #392 |
feat(chat): extend doc-pipeline to xlsx + pptx (Gotenberg path) |
2026-05-23 (Doc-pipeline Sidecars Slice 2 V1 LIVE — pw-os-v2 BFF + frontend integration)
Slice 2 V1 of the doc-pipeline shipped to pw-os-v2 main ~3 hours after Slice 1 landed in pwllc-prod. The advisor-chat BFF (Hono on Cloud Run) now consumes the two in-perimeter Cloud Run sidecars (Gotenberg + Docling) Adam-CCO ratified earlier today via the 5-control CCO-perimeter guarantee. Two PRs landed:
- pw-os-v2
#390(BFF integration): newapps/api/src/lib/doc-converters.tswith OIDC-authed HTTP clients for both sidecars (Gotenberg/forms/libreoffice/convert, Docling/v1alpha/convert/file);DocConverterError({kind: unavailable | timeout | rejected | malformed, sidecar, status, detail})carries the fail-loud signal.attachments.tsextended with docx inSUPPORTED_MIMES+ newconvertInboundDocs(inbound, classification)routing —client-data→ Gotenberg fidelity → PDF → Claude PDF content block;b2b-counterparty→ Docling text-extract → text-prepend (markdown). Two new audit verbs admitted via the 0046 CHECK-widening pattern:chat.attachment.converted(afterStatecarries sidecar / source_mime / output_mime / source_bytes / output_bytes / duration_ms / source_name / output_sha256) +chat.attachment.conversion_failed(afterStatecarries kind / status / detail / sidecar). The chat send route runs conversion AFTERvalidateInbound+ BEFORE the per-conversation byte cap so the cap measures the bytes that actually reach Claude (a small docx can produce a larger PDF). Post-conversion size cap re-check added — a docx that expands past MAX_FILE_BYTES post-convert 413s with akind: 'output_too_large'audit row beforematerializeAttachments' post-decode guard would 5xx. On anyDocConverterError: 502 to caller + audit row capturing the kind + status. Two pre-merge CI fix-forwards on the same branch: (a) the repo'sSecurity checkworkflow grepsBearer [a-zA-Z0-9]acrossapps/**/*.tsexcluding__tests__/only, and my'Bearer mock-oidc-token'test literal tripped the grep — rewrote the assertion tosplit(' ')so the literal "Bearer" pattern no longer appears in the test file (same coverage); (b) the post-conversion size cap re-check landed in the same fix-forward commit. 1420 api tests + full-repo typecheck green; ESLint + compliance lint clean. - pw-os-v2
#391(frontend accept): ChatPageSUPPORTED_MIMESset +<input accept=...>extended to docx. This PR was orchestrated separately because the frontend commit raced the auto-merge of#390(auto-merge fired the moment the security-check pass landed, before the frontend push landed).#391carries only the frontend change; the BFF half is in#390. CI had one transient flake on the Tests job (actions/checkout@v6auth failure —fatal: could not read Username for 'https://github.com') that was re-run successfully.
UPDATE — Slice 2 V1 FULLY WIRED + PRODUCTION-VERIFIED (~30 min after #391 merged). pw-infrastructure #203 authored + operator-merged + operator-applied — adds DOC_CONVERTER_GOTENBERG_URL + DOC_CONVERTER_DOCLING_URL env vars on the pw-os Cloud Run service template, sourced from the sibling sidecar resources in the same workspace (so an image bump that re-issues a revision URL flows through automatically on the next apply). One TF apply contention worth capturing: the post-merge CI Terraform plan job acquired the GCS state lock while the operator's plan was queueing → operator got Error 412 conditionNotMet → lock released cleanly when CI completed → operator re-planned + applied successfully. The 6-resource modify (1 functional + 5 scaling-block reconciliation no-ops matching the Slice 1 pattern) completed. New pw-os revision pw-os-00345-pvx serving 100% traffic with both env vars present (verified via gcloud run services describe). Operator smoke-tested at pwos.app/chat by uploading a junk-content docx — Claude received it as a PDF content block and read the text layer accurately ("That PDF doesn't have any real content; it's just keyboard mash: 'Glnngjgmer / Geklgjklfbjgf...'"); end-to-end proof: frontend accept → OIDC mint → Gotenberg /forms/libreoffice/convert → %PDF magic check → Claude PDF content block → text-layer preserved. The whole conversion ran inside the project perimeter — bytes never left the GCP network. P1.26 (the env-vars operator follow-up) SHIPPED 2026-05-23 evening.
Deferred Slice-2 follow-ons (P1.22b): Drive/gdocs resolution (export-as-docx via the existing DWD-impersonated Drive client → sidecar pipeline) + per-conversion PII canary on Docling-extracted text (the b2b-counterparty path is canary-overridden by attestation; client-data → Gotenberg → PDF inherits the existing PDF non-scannable posture per routes/chat.ts "attachments are NOT PII-scanned in v1" comment).
Trust posture (unchanged from Slice 1): the sidecars run with all 5 CCO-ratified controls. Slice 2 consumes that perimeter; it does NOT introduce new external dependencies.
Estate-wide PRs (3 — 2 pw-os-v2 + 1 pw-infrastructure)
| Repo | PR | Title |
|---|---|---|
| pw-os-v2 | #390 |
feat(chat): doc-pipeline Slice 2 — sidecar integration for docx attachments |
| pw-os-v2 | #391 |
feat(chat-ui): accept docx in advisor file picker |
| pw-infrastructure | #203 |
feat(pw-os): wire DOC_CONVERTER_{GOTENBERG,DOCLING}_URL on pw-os Cloud Run |
2026-05-23 (Doc-pipeline Sidecars Slice 1 LIVE + Overnight Diagnostic + 204-2 Classification Matrix + Hygiene Train)
Eight PRs landed across five repos in one iteration; two sidecars went from greenfield to LIVE in pwllc-prod; the cross-repo 204-2 books-and-records posture was scope-corrected with a CCO-ratified classification matrix. Day organized around four threads:
Doc-pipeline Sidecars — Slice 1 LIVE (Gotenberg + Docling, in-perimeter). pw-infrastructure
#201shipped the TF DRAFT (twogoogle_cloud_run_v2_serviceresources + sharedpw-doc-converter-serviceSA + scopedrun.invokerIAM). Image defaults digest-pinned to operator-verified revisions (gotenberg/gotenberg:8@sha256:a40c5a46…v8.32.0 MIT-licensed;docling-project/docling-serve:v1.19.0@sha256:ad8259f3…v1.19.0 MIT-licensed). Adam-CCO ratified the 5-control CCO-perimeter guarantee (INTERNAL_ONLY ingress + PRIVATE_RANGES_ONLY egress + zero-vendor-creds + no-persistent-storage + scoped-invoker) via a bare "approved" PR comment at 2026-05-23T15:48:48Z; operator merged 6 minutes later. Operator's initialterraform applyfailed on the Docling create — Cloud Run's image-pull host allowlist rejectsquay.iodirectly (onlygcr.io/docker.pkg.dev/docker.ioaccepted). pw-infrastructure#202fix-forward added agoogle_artifact_registry_repository.quay_remote(REMOTE_REPOSITORY mode proxying https://quay.io) + IAM grant to the Cloud Run service agent + re-routed the Docling default through the AR coordinate. Operator's re-apply succeeded; both sidecars are LIVE (Ready=True) athttps://pw-doc-converter-gotenberg-hifyv7pcxq-uc.a.run.app+https://pw-doc-converter-docling-hifyv7pcxq-uc.a.run.app. Books-and-records record atcompliance/cco-approvals/PW-DOC-PIPELINE-SLICE-1-2026-05-23-APPROVAL.md. Verification finding: Cloud Run v2's INTERNAL_ONLY denial returns 404 (not 403) —/healthprobes from external + Cloud Shell sources all return 404 by design (security-by-obscurity variant). Startup probeReady=Trueconfirms/healthIS responding 2xx from inside Cloud Run's authorized network. Memory:doc-pipeline-slice-1-live,cloud-run-host-allowlist-rejects-quay-needs-ar-remote-mirror,cloud-run-internal-only-returns-404-not-403-for-denial.Overnight Diagnostic + 204-2 Classification Matrix (the books-and-records reframe). A read-only overnight diagnostic ran across the estate (no code-repo writes; 10 findings docs landed at
strategy/diagnostics/2026-05-23-overnight/via #364). The dominant finding (B4-1) scope-corrected an existing governance-ledger entry — the Altruist-callback "WORM-mirror gap" was actually estate-wide (~50% ofaudit_logrows PG-only becausewriteAuditMirror()lives only inpw-api/src/lib/audit.ts; 46 pw-os-v2 BFF sites + 1 pw-portal-v2 + 3 raw-SQL pw-api sites bypass). A Phase-1 read-only classification matrix landed via#365— every distinct audit class (320 total) classified into one of three buckets (RECORD_204_2 / EVIDENCES_204_2 / OPERATIONAL) with proposed-bucket + empty cco_ratified_bucket columns. Headline carve-out: of the 100 bypassed classes, 71% are OPERATIONAL telemetry with no 204-2 archive obligation (per-chat-turnagent.context.*,ai.tool.called, auth events, etc.) — the real archive gap is 29 classes (7 RECORD_204_2 + 22 EVIDENCES_204_2). Regulatory frame corrected: Protocol Wealth is a standalone SEC RIA under Advisers Act Rule 204-2 (reasonable controls), NOT a broker-dealer under Exchange Act Rule 17a-4 (WORM media); the GCS bucket is a best-practice control we hold ourselves to, not a per-se requirement. CISO determination on remediation path + NORMAL timeline DATED 2026-05-23; CCO ratification block intentionally EMPTY + UNDATED for Adam's review. Two CISO-side governance-ledger determinations CLOSED: Interns 1099-NEC classification (CTO/CISO-autonomous; internal tooling; no client NPI) + B5-1 PII_TAGS drift gate hardening (engineering substrate). Memory:worm-mirror-coverage-gap-is-estate-wide,market-scan-profile-source-sector-never-in-schema.B5-1 PII_TAGS cross-repo drift fix (HIGH). pw-portal-v2
#77hardened the PII_TAGS cross-repo drift gate. The portal had 3 missing table blocks vs the pw-api canonical (account_balancesfrom migration 0055;signed_document_archive+signed_document_envelope_eventsfrom migration 0053;chat_sessionsfrom migration 0054 — the diagnostic counted 2, reality was 3). Root cause: the portal's pre-fix CI gate invoked the bash drift script againstraw.githubusercontent.comwhich 404s on private repos without auth; the CI step degraded that exit-2 to a non-blocking warning, so drift landed silently between every pw-api canonical update and the next portal PR. Fix mirrors pw-os-v2 exactly: newapps/api/scripts/check-pii-tags-drift.mjsNode wrapper using GitHub Contents API + Bearer auth (private-repo compatible), delegates to the vendored bash script viaPII_TAGS_CANONICAL_URL=file://.... Three missing blocks ported byte-aligned with pw-api canonical. Gate-trips-on-drift verified locally + by CI gate green. Memory:pii-tags-portal-drift-gate-checks-fixture-not-cross-repo(updated with corrected mechanism).Hygiene train (gate-free; 3 bundled-PR items). pw-os-v2
#389bundled three items in one PR: (a)/admin/internshub-link card added to AdminHubPage TILES (GraduationCap icon); (b) static/auth-requiredmessage page (operator-scoped to static copy only — no auto-redirect-to-login, no returnTo, no deep-link capture); (c) B1-1 partner-titles centralized inlib/partners.tsasPARTNER_TITLES = ['CCO', 'CIO', 'CTO']+hasPartnerTitle()helper;chat.ts:isPartnernow delegates. B1-1 flagged finding: the 3-title chat-share gate IS load-bearing, NOT drift —chat.test.ts:155-156explicitly assertsisPartner({titles:['CFP']}) === false+isPartner({titles:['CISO']}) === false. The PR closes the cross-FILE drift inside pw-os-v2 but preserves the cross-REPO 3-vs-5 distinction (chat-share narrower than pw-api governance 5-title list) as intended design. pw-api#273shipped B3-1 (evm.ts API-key reads through Zod-validatedenvinstead of rawprocess.env). B2-1 (oauth_state_nonces RLS) was stopped + flagged — the diagnostic finding was incorrect; the table is intentionally NOT RLS-scoped per migration 0014 + integrations.ts:695 (webhooks consume path has no session context). Branch sweep: 4 stale pw-infrastructure post-merge branches deleted; pw-website's 3 dependabot PRs (#108/#109/#110) skipped per brief.
Estate-wide PRs (8: 5 shared, 1 pw-portal-v2, 1 pw-os-v2, 1 pw-api, 2 pw-infrastructure)
| Repo | PR | Title |
|---|---|---|
| shared | #364 |
docs(diagnostics): land 2026-05-23 overnight diagnostic + scope-correct WORM gap |
| shared | #365 |
docs(governance): Phase-1 204-2 audit classification matrix + close 2 CISO determinations |
| shared | #366 |
docs(cco-approvals): capture Adam-CCO doc-pipeline Slice 1 approval + close in dispatch queue |
| pw-portal-v2 | #77 |
fix(pii-tags): port missing canonical blocks + harden cross-repo drift gate |
| pw-os-v2 | #389 |
chore(hygiene): /admin/interns hub link + auth-required page + B1-1 partner-titles centralized |
| pw-api | #273 |
chore(hygiene): B3-1 — route evm.ts API-key reads through Zod-validated env |
| pw-infrastructure | #201 |
feat(doc-converter): Slice 1 TF DRAFT — Gotenberg + Docling in-perimeter sidecars (CCO ratified) |
| pw-infrastructure | #202 |
fix(doc-converter): route Docling image through AR remote-mirror (Cloud Run rejects quay.io) |
2026-05-23 (Interns module — end-to-end LIVE: pw-api substrate + pw-os-v2 UI)
The Interns module shipped end-to-end across two repos — a 1099-NEC contractor engagement tracker for the 2026 summer interns, with a load-bearing FEE-model invariant encoded in the schema (forbidden-columns CI test), milestone "Mark paid" as a STATUS-ONLY record (no payment rail), and a status-only document checklist (no SSN/EIN/W-9 content fields). pw-api #271 shipped the substrate: migration 0057 added 5 intern_* tables (engagement, milestone, status_update, evaluation, document) with tenant-RLS ENABLE+FORCE, a classification CHECK ('independent_contractor') invariant, 7-year retention_until default; 7 canonical intern.* PW_ACTIONS verbs (engagement.created/updated, milestone.accepted/paid, evaluation.recorded, status.received, document.received) admitted via the migration-0046 CHECK-widening pattern; 9 routes under /v1/internal/interns/* mirroring the FEE-model service with a deliberate FLAT /documents/:docId path (globally-unique docId, not nested under engagement); deterministic 15-business-day payment-due-date helper (lib/business-days.ts); seed of both engagements with total_fee_cents = 250000, midpoint 100000 / final 150000 / architecture_proposal 0; load-bearing intern-migration-forbidden-columns.spec.ts blocks any column name from {wages, hourly_rate, hours_per_week, withholding, payroll, salary, benefits, W-2, FICA, PTO, ssn, ein, itin, tax_identifier} from appearing in any intern_* table. Deployed as pw-api-00261-7qw. pw-os-v2 #388 shipped the consumer: BFF at /api/advisor/interns/* proxying the 9 producer routes 1:1 (including the FLAT documents path); isPartner directly imported from chat.ts (no lib/role-guards.ts extraction — direct import resolved cleanly, no circular dep) gating every mutation; /admin/interns React roster + /admin/interns/$engagementId detail page with editable github_username Day-1 backfill, milestone Accept/Mark-paid actions, weekly status-updates log, 5-dim evaluation rubric editor (technical / systems / communication / judgment / initiative; 1-5 scores), document checklist surfacing ONLY status / doc_kind / received_at / records_pointer (no file-upload UI); UI guards encoded — "Records-only — no payment rail" banner on the detail page + "Records payment; does NOT initiate a transfer" subtitle on the Mark-paid button (the FEE-model classification guard visible in the UI). 28 new BFF tests; suite at 1397 pass / 11 skip. Deployed as pw-os-00341-mw2. The orchestrator pre-verified the contract pinned in cli2's dispatch by reading pw-api's src/routes/interns.ts from origin/main byte-for-byte against cli1's archive §8 — one informational divergence noted at runtime: the producer returns 401 (not 400) on missing X-PW-Actor-Email, unreachable from BFF callers since the BFF always attaches the header. Two ritual #7 branch-switch race validations this iteration (cli1 + cli2 both caught + recovered cleanly via the [ "$(git branch --show-current)" = "<branch>" ] && git commit assert-in-commit pattern; this iteration's prevent-not-recover hardening landed in the cli2 dispatch + practiced on the orchestrator-side dispatch PRs themselves). Memory: interns-module-producer-consumer-split + claude-code-branch-switch-race-assert-in-commit.
Estate-wide PRs (2: 1 pw-api, 1 pw-os-v2)
| Repo | PR | Title |
|---|---|---|
| pw-api | #271 |
feat(interns): module substrate (1099-NEC contractor engagements) |
| pw-os-v2 | #388 |
feat(interns): /admin/interns UI + BFF (pw-os-v2 consumer of pw-api #271) |
2026-05-22 (Share UX — partner quick-selects on advisor-chat Share dialog)
Partner quick-selects shipped on the async-shared advisor-chat Share dialog — a one-click "Add CCO/CIO/CTO" shortcut beside the email-typed add row, reducing partner-share setup from per-email typing to a single dropdown pick. Built on the 2026-05-22 async-shared substrate (#384 + #385). pw-os-v2 #387 — the Share dialog Partners shortcut surfaces the 3 partner emails (resolved from session.titles) as clickable rows; clicking adds a partner to the conversation in one call, identical wire shape to the typed-email flow. Engineering-substrate; no CCO perimeter.
Estate-wide PRs (1, pw-os-v2)
| Repo | PR | Title |
|---|---|---|
| pw-os-v2 | #387 |
feat(chat): partner quick-selects + Partners shortcut on the Share dialog |
2026-05-22 (Async-shared advisor chat sessions LIVE)
Async-shared chat sessions shipped — a partner-owner can now share a pwos.app conversation with other partners who read + contribute over time, with per-message attribution. Two pw-os-v2 PRs landed back-to-back: #384 (backend) and #385 (UI). Backend deploy ran migration 0055 successfully — new conversation_participants table (partial-unique on active bindings; partial idx for the sidebar widen); messages.author_email added + backfilled deterministically from each owning conversation's user_email (every pre-share user message was the owner by single-owner invariant); 3 new audit verbs (conversation.shared_with, conversation.participant_revoked, conversation.participant_left) admitted via the migration-0046 CHECK-widening pattern. Read sites in conversations.ts widened consistently via one shared SQL fragment ownerOrParticipantClause() applied at listConversations + getConversation + listMessages; owner-only ops kept (archive / title / tags / session_mode / supersedeMessagesFrom) — participants read + contribute, they do not administer. UI: a Share dialog (owner sees add-a-partner + per-row revoke; participants see self-leave), a Users icon on "shared with me" sidebar rows, an author short-name on user-message turns when it's a different partner, client-side canEdit aligned with the data-layer guard on supersedeMessagesFrom. NOT real-time — no WebSocket fan-out, no presence; existing per-request SSE stream unchanged; other participants see new messages on next load. Sharing partner-only (CCO / CIO / CTO via session.titles + role === 'advisor'); sharee restricted to @protocolwealthllc.com at the BFF. Retention + PII + GCS attachment scoping inherit unchanged (one conversation = one 7-year timer; attachments keyed by conversation_id, so participants gain access to existing attachments automatically). +17 backend unit tests (1369 total). Diagnostic Phase 1.5 (read-only) drove the design.
Estate-wide PRs (2, pw-os-v2)
| Repo | PR | Title |
|---|---|---|
| pw-os-v2 | #384 |
feat(chat): async-shared chat sessions — owner + active participants (backend) |
| pw-os-v2 | #385 |
feat(chat): UI for async-shared chat sessions |
2026-05-22 (/admin/altruist client picker truncation fix + /v1/internal/clients/search typeahead)
The /admin/altruist link picker no longer truncates clients past the 50 newest — root cause was the picker (and the /clients page) reading /api/advisor/clients → pw-api /v1/internal/clients/list whose default LIMIT 50, ORDER BY created_at DESC silently dropped everything past the 50 newest rows. With the firm at ~100+ clients (after today's Wealthbox import) most pre-existing clients fell off, including 6 of the imports themselves. Replaced the unsearchable all-clients dropdown with a search-as-you-type combobox backed by a new pw-api typeahead endpoint. pw-api #269 added GET /v1/internal/clients/search?q= — case-insensitive substring ILIKE on first / last / full-name / email, same client universe as /clients/list (deleted_at IS NULL AND client_type != 'Archived'), cap default 30 / max 50, short-q (<2 trimmed chars) returns empty without a DB round-trip; integration test covers each match field + case-insensitivity + cap + short-q + Archived exclusion. pw-os-v2 #383 added the BFF proxy + a reusable ClientSearchCombobox (debounced 250ms, keyboard nav, monotonic-request-id stale-response guard, rows showing name + email + Wealthbox ID) wired into /admin/altruist (replacing the <select>) and into /clients (as a "Jump to a client" header box that reaches every client past the still-capped table). One CI re-run on pw-api #269: the first cross-tenant assertion in the route-level integration test could not hold because the CI integration env connects as the pwapi owner role so RLS is bypassed; rls.spec.ts already owns RLS coverage. Both PRs merged + deployed; operator confirmed "all clients are now showing". Memory: clients-list-limit-50-truncation-and-search, pw-os-v2-route-tests-bypass-rls-in-ci.
Estate-wide PRs (2: 1 pw-api, 1 pw-os-v2)
| Repo | PR | Title |
|---|---|---|
| pw-api | #269 |
feat(clients): add /v1/internal/clients/search typeahead endpoint |
| pw-os-v2 | #383 |
feat(clients): search-as-you-type client picker on /admin/altruist + /clients |
2026-05-22 (Wealthbox bulk client import — pwos.app front door)
Wealthbox bulk client import gained its production front door on pwos.app — pw-api #268's import route was unreachable externally (pw-api is INTERNAL_ONLY), so the pw-os-v2 BFF service-account OIDC path is the only working caller. pw-os-v2 #382 added a POST /api/advisor/clients/import-from-wealthbox BFF proxy + a /clients/import dry-run-then-confirm UI: Preview fetches + classifies every active Wealthbox client (writes nothing), renders the count tally (fetched / created / linked / already_present / skipped_no_name / skipped_email_conflict / errors) with a sample table; the Run button stays disabled until a Preview ran this session and shows a confirm dialog before the real run. Operator ran it once at 20:10Z: 56 fetched, 55 created (client_type = 'Client', explicit in the INSERT), 1 linked (kept its pre-import client_type), 0 skipped, 0 errors. pw-api owns the audit row (internal.clients.import_from_wealthbox); no new audit verbs at the BFF. The service-account OIDC pattern reused verbatim from the existing audit-trail / detail / metadata / merge clients-admin routes (Cloud Run metadata-server ID token via fetchIdToken(PW_API_BASE_URL) + X-PW-Actor-Email / X-PW-Actor-Role attestation).
A read-only diagnostic later in the day investigated an operator report that the 56 imports + old portal-clients appeared as "Prospect" in /clients — concluded the 55 imports are correctly client_type='Client' per the import code (every layer of the chain returns the raw column; Cloud Logging confirmed created=55 on the operator's run); old portal-stub clients are 'Prospect' by default per migration 0001 and are not promoted by any UI today (no client_type editing surface on pwos.app — the lifecycle gap is deferred follow-up work).
Estate-wide PRs (1, pw-os-v2)
| Repo | PR | Title |
|---|---|---|
| pw-os-v2 | #382 |
feat(clients): pwos.app front door for Wealthbox client import |
2026-05-22 (Wealthbox client import — analysis memo + route built + deployed)
The missing path to backfill the pw-api clients table with every active Wealthbox client was analysed, built, and deployed — POST /v1/internal/clients/import-from-wealthbox, behind a dry_run probe. Until now the clients table was populated only by the clients.create tRPC mutation (advisor manual) and the portals/provision create_if_missing branch, so the firm's ~45 active Wealthbox clients were absent from pw-api. The import is built and deployed but not yet run — execution is a deliberate operator action.
Estate-wide PRs (2)
| Repo | PR | Title |
|---|---|---|
| pw-shared | #354 |
docs(diagnostics): Wealthbox import-all-clients findings + options memo — MERGED |
| pw-api | #268 |
feat(clients): import all active Wealthbox clients — MERGED + deployed |
What advanced
- Analysis (shared #354). A read-only investigation confirmed there is no existing bulk-import path and no "portal-only filter" — the gating is structural: the
clientstable fills only via manual create + portal-provisioncreate_if_missing. Memo:strategy/diagnostics/2026-05-22-wealthbox-import-all-clients-analysis.md. Operator decision locked: import as plainclientsrows, with portal provisioning kept decoupled. - Route built + deployed (pw-api #268).
POST /v1/internal/clients/import-from-wealthbox— fixed filtercontact_type=Client+status=Active+type=person(confirmed from the canonical Wealthbox API doc); idempotent upsert keyed on theidx_clients_wealthboxUNIQUE index; email-collision handled (LINK an unlinked email match, SKIP + report an ambiguous one);wealthbox_contact_idset on insert so nowb.contact.createoutbox echo fires; no portal / auth / invite side effect. Audited via the canonicalCLIENTS_CREATE/CLIENTS_UPDATEverbs with awealthbox_importorigin tag. 13 new tests; CI green; deployed to thepw-apiCloud Run service (revisionpw-api-00258-qmg). The stale comment naming a non-existent worker as the sole client-row creation entry point was corrected in passing. dry_runprobe.pw-apirunsingress: internal, so a live WealthboxlistContactscannot resolve from a developer machine; the route carries adry_runflag that fetches + classifies + returns the plan without writing. The operator runsdry_runfirst from inside the VPC to confirm the filter + count, then runs for real.
Open operator action
ROWS IMPORTED: 0 — the import has not been run. Execution (dry-run → real) is a deliberate operator action pending an invoke path into the internal-ingress route; a pwos.app admin trigger is a queued follow-on. Memory: pw-api-ingress-internal-unreachable-from-devbox.
2026-05-22 (pwportal resync — diagnostic + scope lock)
The pwportal "Refresh" control was diagnosed: it only re-displays already-stored balances — no on-demand sync runs. The on-demand Quiltt-refresh substrate exists in pw-api but is unwired to the portal control. Diagnostic-only; implementation not built.
Estate-wide PRs (pw-shared)
| Repo | PR | Title |
|---|---|---|
| pw-shared | #355 |
docs(diagnostics): pwportal resync has no workflow — diagnostic + confirmed scope — MERGED |
What advanced
- Diagnosis. The portal "Refresh" button re-queries stored balances rather than triggering a Quiltt account refresh; the pw-api on-demand Quiltt-refresh path exists but the portal control was never wired to it. Diagnostic:
strategy/diagnostics/2026-05-22-pwportal-resync-diagnostic.md. - Scope locked — Option B. The fix wires the portal control to the existing pw-api Quiltt-refresh path and adds a dedicated client-facing audit verb. Cross-repo (pw-portal-v2 → pw-api); client-facing → RIA-compliance gate. Implementation is a separate iteration — not built.
Next
Resync implementation (Option B) is queued. The related client-facing "Disconnect account" feature (pw-portal-v2 + pw-api, CCO-gated) remains queued.
2026-05-22 (Local branch-cleanup sweep — three code repos)
A safe-delete branch-cleanup sweep removed 167 stale local branches across pw-api, pw-os-v2, and pw-portal-v2 — every deletion backed by two independent proofs: a git patch-id match against main plus a merged-PR head-ref cross-reference. The three code repos had each accumulated 45–142 local refs left behind by squash-merged PRs whose remote branches were already pruned; the orphaned refs were tripping fresh sessions into re-anchor cycles.
- Method. The naive
git diff origin/main <branch>empty-tree check is near-useless here —mainadvances after every squash merge, so the tree diff is never empty. Each candidate was instead proven merged by synthesizing its net patch as a commit on the merge-base and confirming the patch-id is already inmain, then independently cross-checked against the repo's merged-PR head refs. A branch was deleted only when both proofs agreed. - Result. pw-api 55 → 30 (25 deleted); pw-portal-v2 45 → 13 (32 deleted); pw-os-v2 142 → 32 (110 deleted). Kept: 68 branches with a live remote (open PRs / active work) and 3 with genuinely unmerged local content.
- One branch held for operator review — pw-portal-v2
docs/promote-adr-b2b-accepted-pattern-8-canonical: its tip is a true ancestor ofmain(the work landed via PR#72under a different head ref) so it is safe, but the head-ref cross-reference did not match — held rather than auto-deleted. - shared was skipped — its working tree carries operator WIP and 11 active locked agent worktrees; a branch sweep there is deferred to a clean-tree pass.
Housekeeping under CLAUDE.md §1.4 standing authority — no repo-code or remote-branch changes. Queue item "stale merged-branches sweep" updated: pw-api / pw-os-v2 / pw-portal-v2 done; pw-website + pw-infrastructure still pending.
2026-05-22 (Altruist OAuth callback restored)
The Altruist OAuth callback was restored to production — it was never gone, only unreachable: the handler survived the pwdashboard.com retirement intact, but two redirect-URI constants still pointed at the retired domain and the route was mounted under /webhooks/* while Altruist redirects to root /auth/altruist/callback. Investigate-first confirmed the full Altruist integration (handler, token exchange + refresh, AES-256-GCM oauth_connections storage, sync + reconciliation/status endpoints, 6 secrets) survived the retirement — the fix is a narrow wiring correction. pw-api #267 merged + deployed + production-verified; operator confirmed a live Connect-Altruist round-trip end to end.
Estate-wide PRs (1, pw-api)
| Repo | PR | Title |
|---|---|---|
| pw-api | #267 |
fix(altruist): restore OAuth callback redirect URI + route mount — MERGED + deployed + verified |
What advanced
- Premise corrected. The originating dispatch's premise ("route handler unregistered") was off — Phase-1 investigation found the 285-line handler complete; 5 of 6 audited areas fully present, only the route registration effectively broken.
- Single redirect-URI constant.
ALTRUIST_REDIRECT_URIconsolidated inaltruist-client.tsatwebhooks.protocolwealthllc.com/auth/altruist/callback;integrations.ts+internal.tsimport it instead of holding stalepwdashboard.comcopies.redirect_uriadded to the code→token exchange (RFC 6749 §4.1.3). - Route re-mounted. The callback moved to a dedicated
altruistOauthRoutermounted at root/auth/altruist/callback; the LB already forwards/auth/*to the webhooks Service, so no infrastructure change. The old/webhooks/auth/altruist/callbackpath is retired. - Audit aligned. Success audit uses
PW_ACTIONS.ALTRUIST_OAUTH_CONNECTED;altruist.oauth.failedis now emitted on token-exchange / persist failures. Two latent audit-SQL bugs fixed in passing (a$3::uuid-into-TEXT cast; aNULLresource_idNOT-NULL violation caught by the new integration test on first CI). - Tests. New unit + integration coverage for the callback flow (restores the deleted TODO
#147coverage).
Compliance note
The callback writes audit via the raw write_audit_log SQL function (post-commit, by design) — this bypasses the GCS WORM mirror the writeAuditLog helper performs. Pre-existing; now operationally live. Captured as a Rule 17a-4 fast-follow for Adam-CCO routing — strategy/diagnostics/2026-05-22-altruist-worm-mirror-gap.md.
Next
Three follow-on workstreams seeded (strategy/diagnostics/2026-05-22-altruist-followon-context.md): import all active Wealthbox clients (pw-api); Quiltt/Altruist duplication → client-facing disconnect (pw-portal-v2 + pw-api); pwportal resync has no workflow (pw-portal-v2). The WORM-mirror fast-follow routes to Adam-CCO. mail-auth Stream A + the posture/display super-prompt remain queued. Memory: pw-api-webhooks-lb-path-routing, altruist-integration-intact.
2026-05-22 (advisor-chat P1 — conservative-power adaptive request mode)
P1 shipped the conservative-power adaptive request mode for advisor chat — a deterministic per-turn planner that picks the model, output-token ceiling, and effort level for every chat turn, with no per-turn LLM router on the hot path. Built on the #378 P0 fix; three pw-os-v2 PRs merged, deployed, and verified live on the pw-os Cloud Run service.
Estate-wide PRs (3, pw-os-v2)
| Repo | PR | Title |
|---|---|---|
| pw-os-v2 | #379 |
feat(chat): planRequest — per-turn adaptive model/effort planner — MERGED + deployed |
| pw-os-v2 | #380 |
feat(chat): wire planRequest into chat routes + claude-client effort param — MERGED + deployed |
| pw-os-v2 | #381 |
feat(chat): advisor chat-mode selector (Adaptive / Max power / Fast) — MERGED + deployed |
What advanced
- planRequest planner (#379).
apps/api/src/lib/plan-request.ts— a pure, deterministic per-turn planner returning{ modelAlias, maxTokens, effort }from cheap signals. Conservative-power policy: default to full power (Opus 4.7); step down to Sonnet 4.6 only for a near-certainly-trivial prompt (exact-match allowlist, ~99% bar) with no document; any document — attached this turn or anywhere in history — forces full power (Opus 4.7 / xhigh). 19 unit tests cover every branch. - Wiring + effort parameter (#380). Both chat send paths (
/messages+/edit) plan each turn via planRequest;claude-client.tsstreamMessagenow sends the Anthropicoutput_config.effortparameter. Effort confirmed against the authoritative docs:output_config: { effort: "low"|"medium"|"high"|"xhigh"|"max" }— GA, no beta header;xhighis Opus-4.7-only;highis the API default. ThemaxTokensclamp keeps#378's 32000 ceiling and binds only as a context-window safety net. - Advisor mode selector (#381). A compact Adaptive / Max power / Fast selector replaces the now-vestigial static model selector; the choice is persisted per conversation and sent as
modeOverrideon every send + edit. Default is Adaptive. - The PII egress canary,
#378's error-surfacing, and the 32000max_tokensdefault are unchanged; CI green throughout (1344 tests on the wiring PR).
Next
mail-auth Stream A (pw-api) + the posture/display super-prompt (pw-api → pw-os-v2) remain queued. Memory: effort-param-and-conservative-power-planrequest.
2026-05-22 (advisor-chat P0 timeout fix — Cloud Run 60s→3600s + chat error-hardening)
The advisor-chat multi-document review failure was root-caused to a 60-second Cloud Run request timeout that killed streaming Opus 4.7 responses mid-stream — fixed by raising the pw-os + pw-portal Cloud Run request timeout to 3600s, with chat error-hardening shipped behind it. The originating dispatch's premise (a context-window / max_tokens problem) was wrong: 1M context is native to Claude Opus 4.7 with no beta header, and the SSE parser was already correct — both verified against the authoritative Anthropic docs + the code.
Estate-wide PRs (2)
| Repo | PR | Title |
|---|---|---|
| pw-infrastructure | #199 |
fix(cloud-run): raise pw-os + pw-portal request timeout to 3600s — MERGED + applied |
| pw-os-v2 | #378 |
fix(chat): surface specific stream errors + lower max_tokens to 32000 — MERGED + deployed |
What advanced
- P0 root cause + fix (pw-infrastructure #199).
terraform/cloud-run-baseline/cloud-run-services.tfsettimeout = "60s"on thepw-osandpw-portalCloud Run services ("task#29V1 stability"). A streaming advisor-chat response — a multi-document review especially — runs well past 60s, so Cloud Run hard-killed the request mid-stream and the response never reached the browser. Raised to3600s(the Cloud Run service maximum) on both services; operator-reviewed + applied; verified live at 3600s. - Chat error-hardening (pw-os-v2 #378).
streamErrorClientMessage()maps an Anthropic prompt-too-long 400 (oversized documents) and a request timeout to specific, advisor-actionable messages; every other error still collapses to the generic text so infra detail never reaches the browser (audit finding L2).CHAT_MAX_OUTPUT_TOKENSlowered 128000→32000 (the per-request override is unchanged). Amodel_context_window_exceededstop-reason notice replaces a silent truncation. 1325 tests pass; merged + deployed green. - Verified non-findings. 1M context is native to Claude Opus 4.7 — no beta header (the
context-1m-*beta was Sonnet-4-only); the chat already had the full window. The SSE parser was already correct —pw-os-v2emits single-line JSON SSE events, so the multi-data:-line bug class (pw-portal-v2 #76) cannot occur here. Neither needed a change.
Next
P1 — the conservative-power adaptive request mode (pw-os-v2) — is queued for a fresh session. mail-auth Stream A (pw-api) + the posture/display super-prompt (pw-api→pw-os-v2) are queued. Memory: streaming-chat-failure-check-cloud-run-timeout-first.
2026-05-22 (CES implementation Phase 1 — pw-api substrate MERGED)
CES Implementation Phase 1 built + merged the pw-api-canonical foundation of the CCO-governed Controlled Exception System, per the ACCEPTED design ADR — landed on pw-api main via PR #266. No operational exception is activated; the foundation is inert against an empty cco_approved_exceptions table.
Estate-wide PRs (pw-api)
| Repo | PR | Title |
|---|---|---|
| pw-api | #266 |
feat(ces): CES Phase 1 — pw-api substrate (consolidated A–D) — MERGED |
Phase 1 was authored as a 4-PR linear stack (#262–#265); a manual stacked-merge scrambled it (gh pr merge on a stacked PR merges into the feature-branch base, not main), so the complete, intact Phase 1 was consolidated into a single PR #266 and merged.
What advanced
- PR A — migration 0056 creates
cco_approved_exceptions(mutable, tenant-RLS ENABLE+FORCE) with CHECK constraints enforcing the CCO-ratified invariants (ssn/credit_card+bank_account_number/ein/itinnever exception-eligible; single context;ai_destinationssubset;document_workflow→entity_hash) + a BEFORE INSERT trigger for the Q2 max-5-active-per-operator limit. - PR B — the
pii.exception.*audit namespace (submitted / granted / rejected / invoked / revoked / expired). - PR C —
getActiveExceptions(), a per-tenant in-process-cached lookup; fail-closed on a fetch error. - PR D —
assertEgressCanaryCleanWithExceptions(), the canary CES gate — a hit covered by an active exception is an audited pass-through (pii.exception.invoked); an uncovered hit still fires;ssn/credit_cardare never consulted. The existing canary path is unchanged. - Engineering substrate — no operational exception activated, no compliance interpretation made. CI green (lint/typecheck/unit, migration dry-run, security scans); migration 0056 applies + deploys to the
pw-apiCloud Run service on the merge.
Iteration notes
- The iteration's earlier §6 worktree block (the pw-api tree was parked on a stale merged branch) was cleared by an operator pw-api re-anchor; the run then completed Phase 1. Block diagnostic:
strategy/diagnostics/2026-05-22-ces-implementation-phase-1-worktree-blocked.md. - A first CI run surfaced a Postgres
array_length-empty-array gotcha (an empty-array CHECK evaluated to NULL and passed) — fixed tocardinality()in two CHECK constraints. - A pre-existing broken
pnpm-workspace.yamlon pw-apimain(anallowBuildsblock with nopackages:field, from commit 9a4086a) had silently failed thenpm auditrequired check for every pw-api PR — fixed in#266(packages: ['.']).
Next
Phase 2 (pw-os-v2 / pw-portal-v2 BFF consultation + scan()-layer consultation) and Phase 3 (CCO management UI) follow as separate iterations; per-exception activation needs explicit Adam-CCO approval.
2026-05-22 (pwportal chat UX — markdown rendering + SSE whitespace fixes)
Two pre-existing pwportal client-chat rendering bugs are fixed — the AI Travel Assistant now renders Claude's markdown as formatted HTML, and streamed text no longer loses inter-chunk whitespace. Both were latent under Haiku 4.5's short outputs and surfaced once the client surface moved to Sonnet 4.6 (the chat-policy upgrade, pw-portal-v2 #74). Autonomous engineering-substrate session — no CCO perimeter.
Estate-wide PRs (2; pw-portal-v2)
| Repo | PR | Title |
|---|---|---|
| pw-portal-v2 | #75 |
fix(chat): render assistant markdown in the Travel Assistant chat |
| pw-portal-v2 | #76 |
fix(chat): preserve whitespace across streamed SSE chunks |
What advanced
- Bug A — markdown rendering (PR #75). The chat message component rendered assistant
contentas a raw text node, so markdown showed as literal**,#, and|syntax. Assistant turns now render through react-markdown + remark-gfm into the hand-written.prosestylesheetindex.cssalready carried; user turns stay plain text. GFM table styling added.@tailwindcss/typographywas deliberately not added — the existing.proseclass would have collided (substrate-evidence catch against the session brief). - Bug B — SSE streaming whitespace (PR #76). The chat SSE parser took only the first
data:line of each frame and ran a blanket.trim()— adjacent text deltas concatenated ("walkable" + " it" → "walkableit") and any delta containing a newline lost content. Replaced with a spec-compliant parse: collect everydata:line, strip exactly one framing space, rejoin with\n. - Test infrastructure.
apps/webhad no test runner; vitest 4.x was added (matchingapps/api) with a CI step. 20 web tests cover both fixes.
Next
The pwportal chat UX overhaul iteration is closed; both PRs merged + CI green + deployed on merge-to-main. The CES implementation iteration remains estate priority 1.
2026-05-22 (CES — design ADR ACCEPTED on CCO substantive review)
The CES design ADR is promoted DRAFT → ACCEPTED — Adam-CCO completed the substantive review; the design phase is closed and the CES implementation iteration is unblocked.
Estate-wide PRs (pw-shared)
| Repo | PR | Title |
|---|---|---|
| pw-shared | #341 |
docs(adr): promote CES ADR DRAFT → ACCEPTED — Adam-CCO substantive review (merged) |
What advanced
- CES design ADR ACCEPTED — promoted via PR
#341(Adam-CCO approved + merged 2026-05-21). The seven-dimension design is cleared for implementation. - CCO substantive review complete — Adam-CCO ratified all five open questions with substantive additions on four: Q1 (extend the never-eligible list to
bank_account_number/ein/itin/similar); Q2 (operator standing-exception operational limits — single context,entity_hashrequired for business-contract exceptions, max five active per operator); Q4 (use-it-or-lose-it — 90-day zero-invocation auto-revoke — + CCO anomaly alerts); Q5 (Dimension 5 UI additions — PDF audit-trail export, quarterly report, search, CCO email notifications). These additions are CCO-mandated requirements for the implementation iteration. - CCO-REVIEW books-and-records record — Adam-CCO's substantive review is recorded as a REVIEW entry in
compliance/cco-approvals/(PW-CONTROLLED-EXCEPTION-2026-05-21-ADR-REVIEW.md; operator committer-of-record), closing the ADR's promotion criterion 1.
Next
The CES implementation iteration is unblocked — cco_approved_exceptions table + migration, canary + scan() CES-consultation, pii.exception.* audit namespace, CCO management UI, the Tier 1 roster-tokenization build — operator-merge-gated, carrying the CCO additions above. Per-exception operational activation remains gated on explicit Adam-CCO approval.
2026-05-22 (CES — design ADR revised + books-and-records record committed)
The CES design-phase deliverables are complete pending the CCO's substantive review: the books-and-records authorization record is committed into compliance/cco-approvals/, and the design ADR DRAFT was revised to incorporate the CCO's actual verbal articulation. A continuation of the same-day CES design-phase kickoff (below).
Estate-wide PRs (pw-shared)
| Repo | PR | Title |
|---|---|---|
| pw-shared | #339 |
docs(cco-approvals): CES design-phase authorization record — operator-committed (Rule 17a-4 books-and-records) |
| pw-shared | #335 |
docs(adr): CES design ADR DRAFT revised for the CCO's verbal articulation (open; operator-merge-gated) |
What advanced
- Books-and-records record COMMITTED at
compliance/cco-approvals/PW-CONTROLLED-EXCEPTION-2026-05-21-APPROVAL.md— operator committer-of-record per CLAUDE.md §1.3 + Rule 17a-4 provenance. It anchors the CCO's verbal design-phase approval (2026-05-21 13:34 ET, Google Meet) + the follow-up verbal confirmation of corrected framing. - CCO verbal confirmation OBTAINED — the CCO verbally confirmed the corrected substantive framing in a follow-up call/chat: "purposeful intent" / "case-by-case basis" / "human oversight" as the governing principles; the dual approval pathway (CCO-direct or partner-with-CCO-approval); the dual AI destination (Anthropic ZDR and/or Gemma); approved data types names / addresses / phones / emails.
- ADR PR
#335REVISED — the design ADR DRAFT now reflects the CCO's verbal articulation: partner submission pathway, dual AI destination, and gate-layer coherence (the exception spans the egress canary and thescan()/ tokenization layer — verified from code: names/addresses are not canary hit types). The Context quote was corrected to separate the CCO's substantive verbal intent from the CTO/CISO's engineering synthesis ("scope tuple"). Six ADR open questions reduced to five — grant authority is resolved by the articulation. Status DRAFT; substantive CCO review gates ACCEPTED. - Substrate-evidence-discipline instance
#17RESOLVED — the meeting-notes corroboration gap (the auto-generated Google Meet summary did not capture the CES discussion) is closed: the operator's contemporaneous record + the CCO's follow-up verbal confirmation are the Rule 204-2 books-and-records substrate. The earlier plan to seek a written corroboration was satisfied by the verbal re-confirmation, captured contemporaneously.
Operator next-action
Forward the committed books-and-records record + the revised ADR PR #335 to the CCO for substantive ADR review. On the CCO's substantive review + resolution of the five open questions, the ADR promotes DRAFT → ACCEPTED; the CES implementation iteration is gated on that promotion.
2026-05-22 (CES controlled-exception-system — design-phase kickoff)
Design-phase kickoff of the CCO-governed Controlled Exception System (CES) — a scope-bounded, fully-audited exception mechanism over the PII egress canary. The CCO verbally authorized the design phase; two DRAFT deliverables, no code. After they landed, the operator identified four material content corrections — a revision pass is queued before the documents forward to the CCO for substantive review.
Estate-wide PRs (2; pw-shared)
| Repo | PR | Title |
|---|---|---|
| pw-shared | #335 |
docs(architecture): CES design ADR DRAFT for CCO review (open; operator-merge-gated) |
| pw-shared | #336 |
docs(strategy): sequence the CES iteration — dispatch queue + briefing (merged) |
Deliverables
- CES design ADR DRAFT (
architecture/decisions/ADR-cco-controlled-exception-system.md; PR #335) — 7 design dimensions; generalizes the canary's existing narrowb2b-counterpartyclassification override into CCO governance. Dimension 7 recommends consolidating the deferred Tier 1 (names + phones) auto-tokenization. Status DRAFT; promotion to ACCEPTED gated on CCO substantive review. - CES books-and-records approval record — staged at
strategy/drafts/for operator commit intocompliance/cco-approvals/(agent hard-stop path; the operator is committer-of-record per Rule 17a-4 provenance).
Revision queued
Four operator-identified corrections — the CCO's actual verbal articulation vs. operator architectural synthesis; a partner-with-CCO-approval pathway; dual Anthropic-ZDR / Gemma destination scope; a meeting-summary corroboration note — are scoped to a dedicated next-session revision pass. The corroboration note records, per substrate-evidence-discipline, that the auto-generated meeting summary did not capture the CES discussion; the operator seeks the CCO's written corroboration before the canonical books-and-records record commits (cross-system consistency under Rule 204-2).
2026-05-22 (chat model + max_tokens configuration policy — ADR + Phase 2A/2B)
The Track A tool-firing fix was expanded by operator directive into a chat model + token configuration policy across the chat surfaces. Three operator-locked decisions, an updated ADR, and two implementation PRs — all reviewable, operator-merge-gated, open for review.
Estate-wide PRs (3 across 3 repos)
| Repo | PR | Title |
|---|---|---|
| pw-shared | #332 |
docs(adr): PWOS chat model + max_tokens configuration policy (DRAFT) |
| pw-os-v2 | #376 |
fix(chat): raise chat max_tokens to 128000 + surface truncated tool calls |
| pw-portal-v2 | #74 |
fix(chat): default client chat to Sonnet 4.6 + raise max_tokens to 64000 |
The policy
- Default model by surface. Advisor (pw-os-v2) → Opus 4.7 (already the default — no change). Client (pw-portal-v2) → Sonnet 4.6 (was Haiku). Chat-session naming (pw-api) → Haiku 4.5 (preserved; verified, no change).
- Max output tokens by surface. Advisor
CHAT_MAX_OUTPUT_TOKENS = 128000; clientDEFAULT_MAX_TOKENS = 64000.max_tokensis a ceiling — not billed, and verified not to factor into OTPM rate limits. - Truncated-tool_use handling. A
claude.tool_use_truncatedstructured log + an explicit user-facing notice replace the silent discard — closing the Track A Bug-2 silent failure.
Substrate-evidence-discipline
The directive locked 131072 / 65536 as "the model ceilings." The Anthropic docs confirm Opus 4.7 = "128k" / Sonnet 4.6 = "64k" max output (synchronous Messages API, no beta header) — but as "128k"/"64k", not exact integers. 128000 / 64000 were used instead — provably ≤ the cap under both the decimal and binary readings, so they cannot 400 every chat request, while fully achieving the policy intent. ADR: architecture/decisions/ADR-pwos-chat-max-tokens-fix.md.
2026-05-22 (substrate quality sprint — tool-firing investigation + Tier 1 bridge fixes)
A substrate quality sprint: a P0 tool-firing bug investigated to root cause, plus the first Tier 1 bridge fixes from the PWOS substrate self-audit. Track A pinned why PWOS chat write tools silently no-op; Track B shipped 2 of 7 audit bridge fixes and captured the rest with precise deferral reasons.
Estate-wide PRs (5 across 2 repos)
| Repo | PR | Title |
|---|---|---|
| pw-shared | #329 |
docs(diagnostics): PWOS tool-firing bug — root cause + fix spec |
| pw-shared | #330 |
docs(diagnostics): tool-firing bug — production telemetry confirmation (§6.1) |
| pw-shared | #331 |
docs: iteration close — substrate quality sprint |
| pw-os-v2 | #374 |
fix(api): render Wealthbox linked_to objects instead of "[object Object]" |
| pw-os-v2 | #375 |
fix(api): show "Shared Drive" as owner for Shared Drive search results |
Track A — tool-firing bug (P0)
PWOS chat write tools that inline a large content payload (drive_create_doc's initial_content, etc.) silently no-op — the assistant says "creating it now" and nothing happens, no error. Root cause is a two-bug compound: routes/chat.ts never raises maxTokens above the claude-client.ts 4096 default, so a large tool_use truncates mid-emission (stop_reason: max_tokens); the tool loop then discards any tool_use on a non-tool_use stop reason. Three evidence layers converged — code trace, Cloud Logging event sequence, and an operator audit_log query (5 of 9 turns at exactly 4,096 output tokens). The same cap also truncates long plain-text responses. P0; no CCO perimeter. The fix is multi-file with design choices — captured as a next-iteration ADR-DRAFT-then-reviewable-PR item, not shipped. Record: strategy/diagnostics/2026-05-22-pwos-tool-firing-bug-investigation.md.
Track B — Tier 1 bridge fixes
The PWOS substrate self-audit (read-only, ~59 tools; strategy/diagnostics/2026-05-22-pwos-tool-surface-audit.md) surfaced seven bridge bugs. Two shipped: Wealthbox linked_to "[object Object]" (#374) and Drive-search Shared Drive owner "?" (#375). Five deferred with precise reasons: the UUID-scrubber fix is pw-api-canonical and pw-api is on an active feature branch; the Tradier / MBOUM / OpenCover field-mapping fixes live in pw-api and need live upstream API responses pulled first (some "broken" fields are likely sandbox-tier limits, not bugs). Separately, the Altruist OAuth callback (webhooks.protocolwealthllc.com/auth/altruist/callback) was surfaced broken in production — the route handler is unregistered after the pwdashboard retirement — and is captured as the lead pw-api-iteration item.
Substrate-evidence-discipline
Twelve instances across the 2026-05-19→22 arc. This sprint: the directive's example log query carried the wrong date and assumed non-existent log fields; a NEW hypothesis was disproved by tracing code; and the operator's audit_log query added a third converging evidence layer. Memory: 2026-05-22-evidence-layers-converge-on-mechanism, 2026-05-22-substrate-self-audit-pattern.
2026-05-22 (Tier 1 ADR-misread targeted fix — Bug A + Bug B; verified in production)
The Tier 1 auto-tokenization implementation iteration corrected its own ADR rather than building from it. A code-level verification before operator endorsement found the ADR's premise — that scan()'s vendor_tool_result context does not tokenize emails — was a misread of pii.ts: the cited line is from the maskPII() doc-comment (the outbound display masker), not scan() (the egress tokenizer). scan() does tokenize tool-result emails. The 19 production PII-egress-canary email blocks were two unrelated, code-confirmed bugs — both fixed, deployed, and verified clean against a post-deploy Wealthbox-lookup test chat.
Estate-wide PRs (5 across 3 repos)
| Repo | PR | Title |
|---|---|---|
| pw-os-v2 | #372 |
fix(api): scan tool error strings for PII before egress (Bug A) |
| pw-os-v2 | #373 |
fix(api): align canary email allowlist with pii.ts (Bug B) |
| pw-portal-v2 | #73 |
fix(api): align canary email allowlist with pii.ts (Bug B cross-repo sync) |
| pw-shared | #326 |
docs: correct the Tier 1 auto-tokenization ADR misread + bridge docs |
| pw-shared | #327 |
docs: post-merge close — canary verification + dispatch-queue/state sync |
The two bugs
- Bug A — tool error strings bypassed the PII scan. The tool-loop scan sat behind
if (result.ok); a failed tool call's error string reached the egress canary raw. AscrubToolTexthelper now scans + tokenizes both successful output and error strings. - Bug B — the egress canary's email allowlist had drifted from
pii.ts's (4 domains vs 14); vendor-domain emailsscan()passes raw were false-positive-blocked by the canary. The canary allowlist is now a verbatim 14-domain mirror — copied, not imported, so the canary's independent-verifier design is preserved. Synced to pw-portal-v2; the pw-api canary copy is deferred (active feature branch).
Verification
A post-deploy Wealthbox-lookup test chat tokenized 3 client emails across 4 canary-clean turns with zero pii_egress_canary.blocked events — the scenario that produced the 19 baseline blocks, exercised post-deploy, did not block. Tier 1 roster tokenization re-scoped to client names + phones and deferred as follow-on. Diagnostic + verification records under strategy/diagnostics/; memory artifact tier-1-adr-premise-misread-pii-ts-2026-05-22.
2026-05-22 (substrate decisions + capability inventory)
A continuation session off the 2026-05-20 audit remediation produced the substrate-composition foundation. The full-substrate capability inventory mapped the estate across the three-tier (N / G / A) processing model; four load-bearing decisions were recorded; five ADR DRAFTs + two CCO/operator dispatch DRAFTs authored; the EISDIR /assets/* directory-path fix shipped (pw-os-v2 #371). shared/ PRs #318–#325.
- Decisions: cvxportfolio dropped (cvxpy direct, Apache 2.0); per-surface routing via shared library primitives; EMF Python-canonical + TypeScript conformance-tested; chat-attachment retention RESOLVED (Adam-CCO Option A). Financial-planning math: BUILD in-house on permissive OSS.
- Records:
strategy/2026-05-21-full-substrate-capability-inventory.md; five ADR DRAFTs underarchitecture/decisions/.
2026-05-20 (audit remediation executed — bundles B1/B2/B3/B5 shipped; 4 pw-os-v2 PRs)
The overnight-audit remediation (dispatched earlier 2026-05-20, entry below) executed orchestrator-direct — the operator selected in-thread execution over worker dispatch and over phase-decomposed Agent-tool subagents. All four engineering-scope bundles merged to pw-os-v2 main; CI + deploys green.
pw-os-v2 PRs (4)
| PR | Bundle | Findings closed | Merged |
|---|---|---|---|
#367 |
B1 | H1 — vendor-update SQL column-name allowlist | 2026-05-20 |
#368 |
B2 | H2 + M1 — Component 10 substitution hardening (pre-substitution canary pass + bounded map) | 2026-05-20 |
#369 |
B3 | H3 + H4 + M3 — PII egress-canary coverage (tool_result SSN, dot-separated SSN, NFKC) | 2026-05-20 |
#370 |
B5 | L1 + L2 + L3 + L5 — data-layer conversation ownership; SSE error-leak; post-decode size guard; full sha256 digest | 2026-05-20 |
Findings closed: 4 HIGH + 2 MEDIUM + 4 LOW
All HIGH (H1–H4) + the bundled MEDIUM (M1, M3) + four LOW (L1/L2/L3/L5) remediated + deployed. api test count 1287 → 1310 across the bundles.
Two scope decisions
- B3 H3 — SSN-only in the
tool_resultbucket. The audit bundled phone;scan()'svendor_tool_resultcontext deliberately lets tool-result phone numbers through (CRM lookups return them), so the canary covers tool-result SSNs only — firing on phones would hard-fail routine advisor chat for no real leak. - B3 H4 — the
pii.tschange deferred.pii.tsis pw-api-canonical with a hard CI sync gate (Contract#3§3.3); the dot-separator fix to itsUS_SSNregex is cross-repo work. The canary change alone closes H4's egress risk.
Remaining (authored, not executed)
M2 (chat.message.superseded immutable audit-log row; CCO-approved 2026-05-20), the canary cross-repo sync (pw-api + pw-portal-v2), and B6 (GCS re-fetch robustness; B5's L4 folded in) remain as authored dispatch packets — strategy/dispatches/ + the audit record strategy/diagnostics/2026-05-20-overnight-substrate-audit.md.
2026-05-20 (overnight substrate security audit + 2 pw-os-v2 production fixes + remediation dispatched; 4 PRs across 2 repos)
Single-instance orchestrator session. Two production fixes shipped + deployed to pw-os-v2; a read-only overnight substrate security audit conducted; the remediation authored as dispatch packets and merged to shared/.
Estate-wide PRs (4 across 2 repos)
| Repo | PR | Title | Merged |
|---|---|---|---|
| pw-os-v2 | #365 |
fix(chat): Agent-memory context 500 — query after_state not a non-existent details column | 2026-05-20 |
| pw-os-v2 | #366 |
fix(chat): enforce the per-conversation attachment-total cap | 2026-05-20 |
| pw-shared | #314 |
docs(dispatch): overnight-audit remediation — engineering-scope dispatch packets | 2026-05-20 |
| pw-shared | #315 |
docs(adr): M2 — chat.message.superseded immutable audit-log row (DRAFT) | 2026-05-20 |
Production fixes (pw-os-v2; both deployed)
#365— Agent-memory context HTTP 500. The Component 7 Week 3 Deliverable 2 endpoint queried adetailscolumn that does not exist onaudit_log(the table carriesbefore_state+after_state); every call 500'd, latent since the endpoint shipped. Fix:after_state AS details+ the endpoint's first test suite (5 cases incl. a regression guard).#366— per-conversation attachment-total cap.MAX_TOTAL_ATTACHMENT_BYTES_PER_CONVERSATION(15MB) was documented but unenforced — a conversation could accumulate unbounded attachment bytes re-fetched from GCS + re-sent to the model every turn. Now rejected with 413conversation_full; 6 new tests.
Overnight substrate security audit
Read-only audit of pw-os-v2 (PII pipeline; chat attach + Components 9/10; auth/RBAC/audit-trail/SQL) via 3 parallel read-only review agents + orchestrator diagnostic. Verdict: codebase fundamentally sound — OAuth trust root, route protection, RBAC, the egress canary's fail-closed behavior, and audit-write fail-closed posture all hold up. Findings: 2 HIGH, 3 MEDIUM, 5 LOW; no CRITICAL externally-exploitable finding. Canonical record: strategy/diagnostics/2026-05-20-overnight-substrate-audit.md.
Remediation dispatched (shared/ #314 + #315)
Seven remediation bundles authored: B1 (H1 vendor-update SQL column-name allowlist), B2 (H2+M1 Component 10 substitution hardening), B3 (H3+H4+M3 canary coverage), B5 (L1–L5 LOW findings), the cross-repo canary sync (pw-api + pw-portal-v2 fast-follow), B6 (GCS re-fetch robustness), and M2 (chat.message.superseded immutable audit-log row). B1/B2/B3/B5 + sync + B6 are engineering substrate — CTO/CISO standalone per cco-gate-scope-substrate-vs-client-facing-2026-05-19. M2 is the sole CCO-perimeter item (SEC Rule 17a-4 / 204-2); CCO-approved 2026-05-20. Execution restructured to orchestrator-coordinated Agent-tool subagents (phase-decomposed under the 600s watchdog) per operator directive; the fresh-CLI worker dispatch packets at dispatch/cli-w*/pending.md remain a valid fallback.
2026-05-19 (post-meeting cycle: positioning anchor codified + /agents 5th Tier-1 surface LIVE; 8 PRs across 2 repos in ~90 min)
Single-instance hybrid orchestrator session ~17:00Z → ~19:30Z (~90 min wall-clock). Per CLAUDE.md §2.0 hybrid orchestration — global state in-thread; 1 worker dispatch (cli3) for substantive pw-website page write per §2.0 Q5; orchestrator-direct on shared/ docs + memory + outcome capture + sanitization. cli3 completed in ~35 min vs 60-90 min estimate (mature inheritance from cli2's /how-we-work pattern; §17 chain depth 0). Anchored by external-advisor meeting outcome: positioning frame "Solution for training AI agents, not launching them" + three-tier memory architecture (per-client / per-advisor / per-firm) as Component 7 substrate target.
Estate-wide PRs (8 across 2 repos)
| Repo | PR | Title | Merged |
|---|---|---|---|
| pw-shared | #286 |
docs(changelog): style policy — no personal names + high-level summary | ~17:30Z |
| pw-shared | #287 |
docs(strategy): external-advisor demo outcome + training-not-launching positioning memory | ~17:45Z |
| pw-shared | #288 |
docs(sanitize): expanded no-names policy across CHANGELOG + COMPLIANCE-CHANGELOG + strategy + memory | ~18:10Z |
| pw-shared | #289 |
docs(sanitize): work-focused outcome rename + drop industry-journalist framing | ~18:25Z |
| pw-shared | #290 |
dispatch(cli3): /agents substrate-for-training page — 5th Tier-1 surface (Week 1 of Component 7) | ~18:46Z |
| pw-shared | #291 |
docs(strategy): pwos.app/agents scope doc + next-iteration-briefing refresh | ~18:50Z |
| pw-website | #117 |
feat(agents): /agents substrate-for-training page (5th build-in-public Tier-1 surface) | ~19:20Z |
| pw-shared | #292 |
chore(dispatch): cli3 task completed; archived | ~19:25Z |
Build-in-public Tier-1 — 5th surface LIVE
https://protocolwealthllc.com/agents— substrate-for-training page anchoring the positioning frame surfaced in the external-advisor meeting. Documents the three-tier memory model (per-client / per-advisor / per-firm) + substrate primitives (PII tagging + audit-log principal chain + RBAC + ADR file index) + verification pathways. Aspirational client-agent framing (substrate enables, surface in build runway). Same disclaimer pattern as /how-we-work (top + bottom + zero personal names + zero performance/AUM/advisory-service refs). Marketing Rule §206(4)-1 bundle umbrella from earlier today's CCO approval covers this 5th surface; per-surface ratification at convenience per batched-routing pattern.
Strategic positioning anchor codified
External-advisor meeting surfaced the frame: "Solution for training AI agents, not launching them. Operating system for the RIA > client portal layer." The 4 Tier-1 surfaces shipped earlier today + the substrate posture they describe collapse into a single positioning frame. Anchored in shared/strategy/2026-05-19-build-in-public-tier1-launch-outcome.md. Three-tier memory architecture (per-client / per-advisor / per-firm; gstack-inspired persona-shared-memory pattern) maps to existing PW substrate primitives + becomes the Component 7 anchor. Anticipated industry article publication mid/late June 2026 = next external-attention moment; build-runway 4-6 weeks queued.
Component 7 scope queued (pwos.app/agents functional surface)
shared/strategy/pwos-agents-functional-surface-scope-2026-05-19.md — 4 decision picks pre-drafted for operator confirmation before week-3 implementation dispatch (data-model mapping; RBAC enforcement; v0.1 vs v1.0 split; chat-infra interaction; recommendations included). Week 2 = reference implementation in pwos-core/examples/rias-agent-substrate/ + ADRs + architecture diagrams. Weeks 3-6 = functional surface implementation; Marketing Rule clearance for client-facing UX (v1.0). BlockSkunk Phase 0 prep (operator 90-min Wed/Thu) queued as parallel work-front per blockskunk-phase-0-prep-parallel-work-front-2026-05-19 memory.
5 new memory artifacts (16 cumulative for 2026-05-19)
training-ai-agents-not-launching-them-positioning-2026-05-19(project) — strategic frame for agent-related scopingrole-only-no-personal-names-policy-scope-extended-2026-05-19(feedback) — no-names policy extended firm-wide; two-tier provenance framework (aggregate role-only operational; named substantive-obligation per Rule 17a-4 / 206(4)-7 / SEC ADV)pattern-convergence-cross-instance-validation-2026-05-19(feedback) — convergent multi-instance strategic analysis is substrate-validation signalblockskunk-phase-0-prep-parallel-work-front-2026-05-19(project) — parallel work-front with /agents Component 7 build; both run through anticipated article window- Companion:
pwos-agents-functional-surface-scope-2026-05-19.mdscope doc — 4 decision picks for operator confirmation
Substrate milestones
- 5th build-in-public Tier-1 surface LIVE — first surface anchored in strategic positioning frame surfaced from external review (vs derived from internal capability inventory)
worker-self-fix-cascades-past-archive-windowmemory validated empirically downstream — cli2's prior PR#113→#114→#115chain made today's /agents PR ship §17 chain depth 0 (inheritance pattern proves out across worker handoff boundaries)- 16 memory artifacts codified in single day — substrate growth on operational + strategic + policy + technical dimensions
- Two-tier provenance framework codified (aggregate role-only operational documentation; named substantive-obligation records per Rule 17a-4 / 206(4)-7 / SEC ADV)
Operator-cost calibration
cli3 shipped /agents in ~35 min wall-clock + ~3 min orchestrator-direct sync (~38 min total worker→PR→merge→archive). Pattern inheritance from cli2's /how-we-work (snapshot pattern + ambient *?raw types + disclaimer component reuse) reduced wall-clock ~50% from estimate. §17 chain depth 0 on PR #117. Orchestrator-direct shared/ PRs (286-292 = 7 PRs) shipped at ~12-min average via standing PR-merge authorization per orchestrator-pr-merge-standing-authorization memory.
2026-05-19 (build-in-public Tier-1 launch + Marketing Rule §206(4)-1 bundle approved + observability access grants; 12 PRs across 4 repos in ~75 min)
Single-instance hybrid orchestrator session 14:54Z → ~16:53Z (~120 min wall-clock). Per CLAUDE.md §2.0 hybrid orchestration — global state in-thread; 2 worker dispatches (cli1 + cli2) for substantive code-repo writes per §2.0 Q5; orchestrator-direct on shared/ docs + small README + CCO bundle stage. cli2 demonstrated cascading §17 SELF-FIX (PR #113 → #114 → #115) for the first time at N≥2 chain depth.
Estate-wide PRs (15 total: 12 in launch cycle + 3 in follow-on /how-we-work cycle)
| Repo | PR | Title | Merged |
|---|---|---|---|
| pw-shared | #274 |
P1: COMPLIANCE-CHANGELOG entry + 5-fact-sheet kit (pandoc/xelatex; 5 MD + 5 PDF + 3 SVG + LaTeX template) | 15:07Z |
| pw-shared | #275 |
P2: cli1 + cli2 worker dispatches + Marketing Rule bundle stage | 15:15Z |
| pwos-core | #36 |
README advisor section + 17-row package-to-regulation map + Apache 2.0 rationale | 15:18Z |
| pw-shared | #276 |
Marketing Rule §206(4)-1 bundle APPROVED (all 6 items; record at cco-approvals/PW-MARKETING-RULE-BUNDLE-2026-05-19-APPROVAL.md) |
15:28Z |
| pw-website | #113 |
feat(build-in-public): /changelog + /factsheets Tier-1 surfaces | 15:35Z |
| pw-shared | #277 |
Langfuse ADMIN access-control-register entry (CCO + CIO observability access) | 15:39Z |
| pw-os-v2 | #360 |
feat(live): pwos.app/live engineering substrate transparency dashboard (1188 LOC; 16 new tests) | 15:40Z |
| pw-website | #114 |
fix-forward 1: snapshot reorg (commit src/content/ snapshots; CI self-contained) | 15:40Z |
| pw-website | #115 |
fix-forward 2: ambient *?raw module types in env.d.ts (decouples tsc from astro state) | 15:43Z |
| pw-shared | #278 |
cli2 archive (pw-website Tier-1 surfaces completed) | 15:49Z |
| pw-shared | #279 |
cli1 archive (pwos.app/live dashboard completed) | ~15:55Z |
| pw-shared | #280 |
Launch-cycle rollup (CHANGELOG + CURRENT-STATE + memory codifications) | ~16:10Z |
| pw-shared | #281 |
dispatch(cli2): /how-we-work 4th-surface dispatch authoring | 16:24Z |
| pw-shared | #282 |
docs(how-we-work): 3 content edits + external-advisor-quote section pre-launch | 16:38Z |
| pw-website | #116 |
feat(how-we-work): plain-English substrate page LIVE (cli2; ~50 min wall-clock; §17 chain depth 0) | 16:48Z |
| pw-shared | #285 |
cli2 archive (/how-we-work completed) | 16:53Z |
Build-in-public Tier-1 LIVE — 4 public surfaces
https://pwos.app/live— engineering substrate transparency dashboard. Substrate health table + Recent landings (top-10 from sanitized shared/CHANGELOG.md) + Verification pathways + Calendly CTA + persistent non-dismissible engineering-transparency disclaimer banner. BFF route + Hono GitHub-API cache (5-min TTL in-memory) + bake-at-PR-author-time static config. Data-source allow-list HARD-enforced.https://protocolwealthllc.com/changelog— sanitized server-rendered cross-repo CHANGELOG. Astro build-time generation; consumer-local snapshot atpw-website/src/content/CHANGELOG.mdrefreshed viascripts/sync-shared-content.mjson prebuild; CI builds against committed snapshot. Public-repo PR refs linked; internal-repo refs text-only. Defensive client-identifier sanitizer aborts build on hit.https://protocolwealthllc.com/factsheets— index card grid + 5 per-slug HTML pages (pwos-at-a-glance, advisor-ai-vendor-audit-checklist, oss-ria-tooling-reading-list, vendor-ecosystem-reference, os-licensing-partner-faq) + 5 downloadable PDFs (50-55KB each).https://protocolwealthllc.com/how-we-work— plain-English philosophical anchor; 4th and final Tier-1 surface. Sections: substrate-vs-vendor framing; why build in public; "shape of what's emerging in advice" (5 quotes from external advisor on AI scandal / coaches with expertise / performance statement of future / not a "channel" / visibly altruistic attracts clients) + 6 conviction paragraphs (open source = no surprise vendor pressure; practitioner-and-builder; retainer/fee-for-service default; treat people differently to treat them the same; profitability through community; willingness to be wrong on the record).
Marketing Rule §206(4)-1 bundle APPROVED
6-item bundle cleared substantive content review (~10-min turnaround via Google Chat; faster than 33-min canonical baseline). Canonical record at shared/compliance/cco-approvals/PW-MARKETING-RULE-BUNDLE-2026-05-19-APPROVAL.md. Items: posture-doc broader distribution + 5-fact-sheet kit /factsheets distribution + /live substrate-transparency posture ratified + /changelog substrate-transparency posture ratified + /factsheets all 5 approved + PII canary operational-completeness posture-doc revision authorized. External announcement channels (newsletter, blog, partner outreach, conference materials) UNBLOCKED. Pattern: CTO/CISO pre-framing of substrate-transparency-vs-commercial-communication classification accelerates CCO decision velocity ~3x on substrate-transparency batches.
Langfuse ADMIN observability access grants
CCO + CIO promoted to ADMIN at Langfuse org "Protocol Wealth LLC" via Settings → Members admin UI (operator-side; no code-repo write). Trigger: unauthorized dashboard.chart error post-SSO-auto-provision (v2 default = VIEWER/NONE; dashboard.chart scope requires MEMBER+). Compensating controls retained: Google OAuth SSO enforcement + username/password disabled + account-linking disabled + Cloud Run INTERNAL_ONLY ingress + OWNER role NOT granted (peer-admin reserved to CTO/CISO). Books-and-records at shared/docs/compliance/access-control-register.md per Rule 17a-4 + 206(4)-7.
§17 SELF-FIX validated at N≥2 chain depth
cli2 cascading fix-forwards in 8 minutes without orchestrator intervention: PR #113 initial → main Tests failed post-merge (sibling shared/ checkout missing) → PR #114 snapshot reorg → main Tests failed again (.astro/types.d.ts not generated pre-tsc) → PR #115 ambient *?raw declarations → main GREEN at sha c7efb8a. cli1 also caught 3 architecture divergences from dispatch spec (apps/api/ not apps/bff/; TanStack declarative routes; bake-at-PR-author-time) — translated all 3 as §0.2 implementation patterns WITHOUT Phase 1.5 STOP (none of the explicit triggers fired). Validates Phase-1.5-vs-§17 boundary: premise-invalidating constraints → Phase 1.5; implementation translation → §17 self-fix.
3 new memory artifacts (5 total today including 2 cli2-surfaced; 11 cumulative for 2026-05-19)
worker-self-fix-cascades-past-archive-window(feedback) — §17 BOUNDARY scales past first-fix; workers poll main push-event CI AFTER archive + fix-forward without orchestrator unblocktsc-noemit-pre-astro-needs-ambient-raw-types(reference) — Astro/Vite CI needs ambient*?rawin src/env.d.ts; 15-line fix; zero runtime impactcross-repo-raw-imports-need-snapshot-not-sibling-checkout(feedback) — snapshot consumer-local + prebuild sync; CI fully reproducible without sibling checkoutchangelog-style-no-personal-names-high-level-summary(feedback) — CHANGELOG style: no personal names; refer to roles; high-level summary; formal sign-off provenance lives in cco-approvals/ where names + signatures are required for audit
Substrate milestones
- First build-in-public production launch (4 public engineering-transparency surfaces; books-and-records under Rule 17a-4 via git-history + GCS WORM substrate)
- First Marketing Rule §206(4)-1 batched approval at <15-min turnaround (CTO/CISO pre-framing pattern validated)
- First §17 SELF-FIX BOUNDARY validation at N≥2 chain depth (cli2's 3-PR chain in 8 minutes)
- First observability-access grants under Rule 17a-4 + 206(4)-7 access-control discipline (Langfuse ADMIN to CCO + CIO)
- First CHANGELOG style policy codified (no personal names; high-level summary; cco-approvals/ holds named provenance)
Operator-cost calibration
15 PRs in ~120 min wall-clock with ~10 operator coordination messages. cli2's 4th-surface (/how-we-work) shipped in ~50 min vs 80-95 estimated (35-45 min under). §17 SELF-FIX cascading worked without orchestrator intervention on PR #113→#114→#115 chain; PR #116 (post-launch) shipped with §17 chain depth 0 (cli2's prior chain made today's fixes durable).
2026-05-19 (mid-day diagnostic follow-up cycle — 6 of 12 TODOs resolved; 4 PRs across 4 repos; 3 new memory artifacts; 17 worktree orphans cleaned)
Single-instance hybrid orchestrator session 12:47Z → 14:50Z (~2h wall-clock). Per CLAUDE.md §2.0 hybrid orchestration filter — global state held in-thread; no worker dispatches; no subagent spawns; all PRs orchestrator-direct. Resolves the highest-impact P1 + P2 TODOs from the overnight diagnostic (shared/strategy/2026-05-19-overnight-diagnostic-followup-todos.md).
Estate-wide PRs (4 across 4 repos)
| Repo | PR | Title | Merged |
|---|---|---|---|
| pwos-core | #35 |
fix(ts): unblock Release build under @types/node 25 (6-package tsconfig override) | 13:07Z |
| pw-api | #250 |
Build(deps-dev): Bump brace-expansion 5.0.5→5.0.6 (dependabot patch; recreate cycle) | 13:16Z |
| pw-infrastructure | #198 |
fix(audit-logs): match gcloud-locked retention_period 220903200 = 365.25d×7y | 14:31Z |
| nexus-core | #28 |
deps: update redis >=5.2.0 → >=7.4.0 (floor bump; zero call sites verified) | 14:49Z |
Plus: pw-api #257 PII canary 3rd copy merged 12:31Z (re-run cycle initiated by cli1 pre-session per TODO 1); pw-api #252 CLOSED by dependabot ("deps updatable in another way") → replacement group pw-api #258 opened with 12 updates (auto-merge ON; pending CI).
Root cause refinements (diagnostic hypothesis → actual)
- pwos-core Release — diagnostic hypothesized "revert PR
#33@types/node 22→25"; investigation showed Release broke 2026-05-15T11:56Z when PR#27first bumped @types/node 22.19.17 → 25.8.0. v25 dropped TextEncoder/setTimeout globals + tightened dynamicawait import("node:*")resolution. Fix is per-package tsconfig override (not version revert) — affects 6 packages with dual browser-fallback/Node-fallback code paths. - pw-infrastructure TF audit-logs apply — diagnostic hypothesized "post-apply notify step"; actual = TF spec
retention_period = 220752000(7×365 days naive math) vs live LOCKED220903200(7×365.25 days fromgcloud --lock-retention-periodleap-year averaging). Every apply tried to reduce the immutable lock by ~1.75 days. Fix = bump TF value to match live (no behavioral change;terraform planreturns no-op post-fix).
3 new memory artifacts codified (operator-approved)
gcs-lock-retention-period-365-25d-math(reference) —gcloud --lock-retention-perioduses 365.25-day averaging; TF spec MUST match live to the second. Affects future immutable GCS WORM buckets (signed_document_archive next).dependabot-rebase-vs-recreate-after-auto-merge(reference) —@dependabot rebaserefuses after auto-merge enable;@dependabot recreateworks (may close+replace PR; re-enable auto-merge on replacement).types-node-v25-removed-global-textencoder(reference) — v25 removed globals; per-package tsconfig fix pattern; do NOT set attsconfig.base.jsonif some pkgs lack@types/node(TS2688).
Worktree cleanup (TODO 6 ✅)
17 orphan worktrees removed across pw-api (4) + pw-os-v2 (10) + pw-portal-v2 (2) + pw-infrastructure (1). Each branch cross-referenced against gh pr list --state all before remove per orphan-cleanup-must-exclude-open-pr-branches memory. shared/ subagent worktrees (11; locked; .claude/-scoped) DEFERRED per diagnostic doc "not urgent" + isolation.
Diagnostic follow-up status
6 of 12 TODOs resolved (1, 3, 4, 5, 6, 12). Open: TODO 7 (ADV cron first-fire verify; 2026-05-20T03:30Z natural fire window); TODO 8 (audit_log Haiku cost telemetry SQL; operator-authorized); TODO 9 (pw-website dependabot majors; operator-deferred per eslint v10 prereq); TODO 10 (ROADMAP path annotations; deferred); TODO 11 (emf-canonical/terminology-map re-verify; deferred); TODO 2 superseded by TODO 3 resolution.
Operator-cost calibration
~2h wall-clock for 6 TODOs resolved + 4 estate-wide PRs + 3 memory artifacts + 17 worktree orphans cleared with ~3-4 coordination messages. Validates §2.0 hybrid orchestration filter at sustained mid-day intensity. No worker dispatches needed; single-instance held the cross-repo state cleanly across pw-api + pwos-core + pw-infrastructure + nexus-core + shared/ surfaces.
2026-05-19 (post-cascade additions — overnight diagnostic + CLAUDE.md §0 + §2.0 + 12 follow-up TODOs; 3 PRs)
Three orchestrator-direct PRs landed post-cascade-close 04:17Z–11:21Z 2026-05-19 in pw-shared. Read-only overnight diagnostic worker (cli1 shape) followed by post-merge investigation of 2 surfaced priorities + canonical CLAUDE.md additions codifying this iteration's hybrid-orchestration learnings.
pw-shared (3 PRs)
| PR | Title | Stream |
|---|---|---|
#267 |
docs(diagnostics): overnight diagnostic report — 2026-05-19T04:02:37Z (606 lines; 9 canonical checks + 7 iter-specific additions; 1 HIGH browser-resolved + 5 MEDIUM + 12 INFO + 4 LOW) | cli1 read-only diagnostic worker (single-instance hybrid; no subagent spawns; 71 PRs cross-correlated across 8 repos) |
#268 |
docs(strategy): overnight diagnostic follow-up TODOs — investigates P2 + P3 (pwos-core Release root cause = @types/node 22→25 NOT actions/checkout v6; PR #257 PII canary blocker = pre-existing flake clients-merge.spec.ts:368 409→200) |
orchestrator-direct investigation + 313-line TODO doc with P1–P4 priority tiers |
#269 |
docs(claude-md): add §0 coding posture + §2.0 hybrid orchestration (1M context era) | orchestrator-direct CLAUDE.md canonical extension |
CLAUDE.md canonical extensions
- §0 Coding posture (NEW; before §1): four-guideline upstream filter every agent runs before any code-affecting action. 0.1 think before coding; 0.2 simplicity first; 0.3 surgical changes; 0.4 goal-driven execution. Each carries a PW corollary (CCO scope pre-bounded; compliance-critical exempt; 83f346b incident; §8 three-strikes prerequisite).
- §2.0 Hybrid orchestration (NEW; inside §2): 4-question filter applied BEFORE the §2.1–§2.8 spawn triggers fire. Hybrid pattern — orchestrator parent holds global state in-thread; subagents for audit-style (§2.1/2.2/2.3/2.6/2.7/2.8) + mechanical (§2.5). §2.4 refactor planning increasingly in-thread. UNCHANGED: §1.3 hard stops, §6 single-agent-per-repo, §8 three-strikes, §7 multi-agent review.
- §11 step 1 patch: "§0 coding posture FIRST, then §1–§13 coordination" — flags the new operational layer.
prompts/orchestrator/daily-start.mdcompanion: cold-start read order references §0 + §2.0; identity block adds hybrid-orchestration default.
Diagnostic substantive findings (full report at shared/strategy/diagnostics/overnight-diagnostic-2026-05-19.md)
/securityHTTP 403: Cloudflare bot protection blocks curl + WebFetch on HTML routes; real browsers fine (operator-verified post-merge). PDF unaffected. Future overnight diagnostics use/browseskill (real headless Chrome) for live-surface health.- pwos-core Release 3× consecutive failure on main: root cause =
@types/node22.19.19 → 25.8.0 (PR #33) breakingnode:crypto+TextEncodertype defs;actions/checkout@v6(PR #32) is fine. Fix: revert PR#33+ pin to^22.x(recommended) OR migrate tsconfig. - pw-api
#257PII canary 3rd copy: blocked solely on Migration dry-run integration test failingclients-merge.spec.ts:368withAssertionError: expected 409 to be 200— the known pre-existing pw-api main CI flake. Auto-merge already enabled (squash). Re-run CI to land. - pw-infrastructure Terraform
push-event failures: 3 consecutive on main despite runtime applies succeeding (ZDR env live; ADV cron ENABLED; Anvil secrets queryable). Post-apply step is the failing actor. pw-api-signed-documents-annual-adv-re-delivery-scanCloud Scheduler: ENABLED +state.code=-1+ emptylastAttemptTime; first fire expected 03:30Z 2026-05-20.- 71 PRs landed clean across 8 repos in 24h window (overnight cascade + post-close additions); no production runtime errors in 24h Cloud Logging window.
Follow-up TODO inventory (12 items; full doc at shared/strategy/2026-05-19-overnight-diagnostic-followup-todos.md)
- P1 pre-demo (by 11 AM ET 2026-05-19): 2 items — re-run PR
#257CI; demote pwos-core Release severity (not demo-blocking) - P2 fix-after-demo (EOD): 4 items — pwos-core Release fix; pw-infra Terraform post-apply audit; repo sync (pw-api + pw-infrastructure on stale branches); manual worktree cleanup (11+ orphans)
- P3 verify-by-2026-05-23: 2 items — ADV cron first-fire verify; Haiku cost telemetry SQL
- P4 operator-deferred: 4 items — dependabot batch; ROADMAP annotations; doc re-verify (
emf-canonical.md+terminology-map.mdlastVerified 2026-04-05); 5 memory artifact candidates
2026-05-19 (Component 4 e-signature + chat-naming + design-system v1.0 + Anvil terraform — full overnight cascade; 33+ PRs across 5 repos in ~6 hours)
Overnight cascade 2026-05-18 evening → 2026-05-19 ~00:30Z; ~6 hours wall-clock end-to-end; 33+ PRs landed across pw-shared + pw-api + pw-os-v2 + pw-portal-v2 + pw-infrastructure. Component 4 e-signature pipeline FULLY LANDED end-to-end (substrate + state-machine + AnvilClient + webhook + advisor surface + client signing iframe + ADRs + policy + runbook). Chat session naming live (cli5 sidebar pivot; first production pw-api Anthropic SDK egress via Haiku-4-5). Design-system v1.0 canonical (pwos.app + pwportal.app warm-light parity). Anvil terraform secrets + IAM + pw-api-env wired. CCO + CIO 46-pick REVIEW PR #242 (24 CCO + 10 CIO + joint EMF.8 + 13 operator-only) merged but responses pending async; Component 4 + chat-naming implementation proceeded with recommendations baked in as v1 canonical assumptions per soft-gate.
pw-shared (13 PRs)
| PR | Title | Stream |
|---|---|---|
#242 |
docs(cco-approvals): 46-point batched CCO + CIO REVIEW — Component 4 + 5 + dashboard + EMF/PWAF + chat-naming + chat-perf | orchestrator-direct CCO+CIO REVIEW |
#243 |
chore(dispatch): Component 4 e-signature 4-stream dispatch pending.md files | orchestrator-direct dispatch |
#244 |
docs(design-system): v1.0 — unified PW design + UX system (pwos.app + pwportal.app warm-light parity) | design subagent |
#245 |
chore(dispatch): integrate design-system v1.0 into Component 4 cli2 + cli3 pending.md | orchestrator-direct dispatch update |
#246 |
docs(component-4-e-signature): 2 ADRs + e-signature-policy + cadence runbook + Anvil vendor map + DD placeholder | cli-shared (6-doc bundle, 2677 LOC) |
#247 |
chore(dispatch): cli-shared Component 4 e-signature 6-doc bundle completed; archived | orchestrator-direct archive |
#248 |
chore(dispatch): chat session naming 2-stream dispatch (cli4 pw-api + cli5 pw-os-v2) | orchestrator-direct dispatch |
#249 |
chore(dispatch): cli1 Component 4 e-signature pw-api 3-PR sprint completed; archived | orchestrator-direct archive |
#250 |
chore(dispatch): cli3 Component 4 e-signature pw-portal-v2 client signing completed; archived | orchestrator-direct archive |
#251 |
chore(dispatch): cli2 Component 4 e-signature pw-os-v2 advisor surface completed; archived | orchestrator-direct archive |
#252 |
chore(dispatch): fix-forward cli3 completion report missing from PR #250 archive commit |
orchestrator-direct fix-forward |
#253 |
chore(dispatch): archive cli5 chat-naming pending.md after PR #353 merge |
orchestrator-direct archive |
#254 |
chore(dispatch): fix-forward cli5 completion report missing from PR #253 archive commit |
orchestrator-direct fix-forward |
#255 |
chore(dispatch): archive cli4 chat-session-naming substrate (pw-api #255 merged) |
orchestrator-direct archive |
pw-api (4 PRs)
#251PR-C4-A — signed_document_archive substrate + 8 canonical actions (3 substratesigned_document.envelope.*+ 3 lifecycleonboarding.signing.*+ 2 sentinel retry pair) + sentinel-row reconciliation day-one + PII_TAGS day-one + BEFORE-UPDATE immutability trigger + 7-year WORM retention via GCS.#253PR-C4-B — AnvilClient + webhook handler (2nd production consumer of Track B' webhook-receiver primitive; Veriff is 1st) + 4-doc envelope state-machine service (IAA + Form ADV 2A + Privacy Notice + IPS).#254PR-C4-C — declined/expired HITL handlers + annual ADV cron (Rule 204-3 annual re-delivery cron + material-change re-execution flow) + WORM mirror + PDF/A-2b archival format.#255chat_sessions substrate — AUGMENT-shape parallel naming + audit metadata layer (chat naming v1). First production pw-api Anthropic SDK egress via Haiku-4-5 — pw-api joins pw-os-v2 + pw-portal-v2 as Anthropic SDK consumer.
pw-os-v2 (3 PRs)
#351— cli2 advisor signed-documents tab + counter-sign + ADV 2A dashboard + §17 PII_TAGS port (byte-equal vendored from pw-api canonical).#352— design-system warm-light surface pass (pwos.app aligned with design-system v1.0 canonical).#353— cli5 chat sidebar pivot (chat-session-naming UI surface).
pw-portal-v2 (1 PR)
#68— cli3 client signing iframe + 9-state machine + remediation flow (Anvil iframe embed; client-facing 4-doc envelope signing).
pw-infrastructure (3 PRs)
#193— Anvil terraform secrets + IAM (ANVIL_API_KEY + ANVIL_WEBHOOK_HMAC_KEY secrets; Cloud Run SA permissions).#194— Anvil pw-api-env wiring (env block on pw_api Cloud Run resource).#195— ANTHROPIC_ZDR_CONFIRMED restore (pw-api needs the env var now that it makes Anthropic calls via chat-naming).
Substrate milestones earned this cascade
- 3rd dual-canonical-action namespace consumer (substrate
*.session.*+ lifecycleonboarding.*.*pattern): KYC + risk-tolerance + signing - 3rd sentinel-row reconciliation consumer (per ADR-gcs-worm-audit-mirror R3): audit_log live + kyc_verifications + signed_document day-one
- 2nd Track B' webhook-receiver primitive consumer (Veriff 1st; Anvil 2nd)
- 1st production Anthropic SDK egress at pw-api (chat-session-naming Haiku-4-5)
Operator action items carry-forward (cli1 PR-C4-C report)
- Cloud Scheduler tick — daily 03:30 UTC POST to
/v1/internal/cron/signed-documents-annual-adv-re-delivery-scan/run-next(terraform wire-up dispatch candidate) - GCS bucket Object Lifecycle Lock verify on
gs://pwllc-audit-archive(already locked 2026-05-02; confirm signed_document writes inherit) - ANVIL_API_URL sandbox→production toggle after CCO C4.6 ESIGN/UETA confirmation
- Dogfood validation — 2 test clients end-to-end through 4-doc envelope
Gap surfaced for next-iteration dispatch
pw-api missing /v1/internal/signed-documents/ HTTP routes* (cli2 callout). cli1's PR-C4-C added lib layer; BFFs return 502 until follow-up adds advisor-facing HTTP routes; same posture as KYC + risk-tolerance from Components 2 + 3 — candidate Tier 0 next-iteration work.
CCO + CIO REVIEW PR #242 — responses async
REVIEW PR merged but CCO + CIO responses pending async at iteration close. Component 4 + chat-naming implementation work proceeded with recommendations baked in as v1 canonical assumptions per soft-gate. 24 CCO picks + 10 CIO picks + joint EMF.8 + 13 operator-only. Responses to be captured via companion APPROVAL doc per canonical batched-routing pattern when received.
Codification candidates queued for next iteration close (parallel subagent authoring)
- Append completion report BEFORE git mv — cli3 PR
#252+ cli5 PR#254fix-forwards; 3rd validation (prior precedent cli-shared + cli1 from Component 3 iteration); ready for ritual#19 - gh search vs --head for branch-name patterns with
/— cli3 broken poll on chat-naming PR surfaced - Workers re-anchor on origin/main before archive — carry-forward from Component 3 iteration
- Edit-before-git-mv pattern — carry-forward from Component 3 iteration
- Raw URL gh api 404 on private repos — carry-forward from prior iteration
- Archive PR-route default per ritual
#15— carry-forward from prior iteration - Simple gh-poll-suffices for substrate gates — carry-forward from prior iteration
Operator-cost calibration
~6 hours wall-clock for 33+ PRs across 5 repos via 4-stream Component 4 cascade + parallel chat-naming dispatch + design-system bundle + Anvil terraform sequencing. Sustained velocity-per-message remained at ~1 surface per coordination touchpoint. Substrate validated end-to-end on first multi-stream Component-class implementation.
2026-05-18 (Component 3 risk-tolerance sprint — 12 PRs + CCO 17-pick batched routing approved + Component 5 scope landed + 6 new memory anchors)
Sprint window ~19:00–20:45Z UTC; ~1h 45min wall-clock end-to-end; 12 PRs landed across pw-shared + pw-api + pw-os-v2 + pw-portal-v2. Component 3 risk-tolerance verification substrate substantively complete (risk_tolerance_sessions + risk_tolerance_responses + risk_tolerance_question_bank + scoring engine + advisor review surface + 7 client assessment screens + ADRs + runbook + compliance policy). CCO approved all 17 batched picks (Component 2 carry-forward + Component 3 first-routing) via PR #227 comment in ~33 min turnaround — validates canonical batched-routing pattern. Component 5 custodian-data scope authored via 3rd START-IMMEDIATELY-validated Agent subagent.
Substantive PRs by stream
| PR | Repo | Title | Stream | Merged | LOC |
|---|---|---|---|---|---|
#229 |
pw-shared | docs(component-3-risk-tolerance): ADRs + compliance policy + runbook + vendor DD placeholders | cli-shared | 19:44:05Z | 1099 LOC across 6 artifacts |
#247 |
pw-api | feat(risk-tolerance): substrate + 8 canonical actions + PII_TAGS + sentinel-row reconciliation (PR-RT-A) | cli1 Task 1 | 19:59:55Z | +1913/-1 |
#350 |
pw-os-v2 | feat(risk-tolerance): advisor risk-profile detail view + bucket-override surface + needs_review escalation | cli2 | 20:13:02Z | 995 insertions |
#248 |
pw-api | feat(risk-tolerance): scoring engine + cadence + advisor override (PR-RT-B) | cli1 Task 2 | 20:13:33Z | 43 new tests |
#67 |
pw-portal-v2 | feat(risk-tolerance): 7 client assessment screens + Veriff-style state machine + BFF routes + PII_TAGS port | cli3 | 20:22:54Z | +2357 (17 files) |
#228 |
pw-shared | docs(component-5-custodian-data): scope doc DRAFT | Agent subagent (3rd START-IMMEDIATELY validation) | 19:35:04Z | 406 lines |
#249 |
pw-api | feat(risk-tolerance): needs_review + integration tests + dogfood + client_assessments bridge (PR-RT-C) | cli1 Task 3 | 20:40:57Z | 11 unit + 5 integration tests |
#227 |
pw-shared | docs(cco-approvals): Component 2 carry-forward + Component 3 first-routing — 17-point batched CCO sign-off | orchestrator-direct CCO REVIEW | 19:52:04Z | 342 lines |
#233 |
pw-shared | docs(cco-approvals): Component 2-and-3 carry-forward 17-pick CCO approval record — CCO approved | orchestrator-direct CCO APPROVAL | 20:38:35Z | 142 lines |
Dispatch + orchestration PRs (pw-shared)
PR #226 (4-stream dispatch pending.md) + #230 (cli-shared archive) + #231 (cli2 archive) + #232 (cli3 archive) + #234 (cli1 archive) + #235 (cli1 content-fix follow-up — validates edit-before-git-mv codification candidate).
CCO 17-pick batched routing — validated
- PR
#227routing: orchestrator-direct CCO REVIEW with 9 Component 2 carry-forward + 8 Component 3 first-routing picks; cc-tag at 19:55:34Z - CCO response: bare "approved" PR comment at 2026-05-18T20:26:06Z UTC — ~33 minutes from cc-tag
- APPROVAL doc filed: PR
#233(142 lines) — canonical Rule 204-2(a)(7) sign-off record - Pattern validates: ~10x operator efficiency at batched scale vs per-pick routing; canonical for future Component 4 + 5 picks (memory
adam-cco-batched-routing-canonical)
Component 3 capabilities now live on main
- risk_tolerance_sessions + risk_tolerance_responses + risk_tolerance_question_bank substrate at pw-api; migration 0051 + 0052 (client_assessments bridge); BEFORE-UPDATE immutability trigger; sentinel-row reconciliation day-one per ADR-gcs-worm R3
- 8 canonical PW_ACTIONS at pw-api/src/lib/audit-actions.ts (3 substrate
risk_tolerance.session.*+ 3 lifecycleonboarding.risk_tolerance.*+ 2 sentinel retry pair) - PII_TAGS.risk_tolerance_* canonical at pw-api; byte-equal vendored in pw-os-v2 + pw-portal-v2
- Deterministic scoring engine at pw-api/src/lib/risk-tolerance-scoring.ts — MIT/Grable FRTS-13 normalize 1-70 + PW overlay 0-30 + composite 1-100 + bucket 1-33/34-66/67-100; golden-fixture test
- Re-assessment cadence cron at Cloud Scheduler daily 03:00 UTC (3-year expiration + material-change + AUM threshold triggers)
- Advisor override surface — assigned-CFP + CCO can override; cross-bucket leaps require CCO co-sign
- needs_review→review_item flow at pw-api/src/lib/risk-tolerance-needs-review.ts — auto-creates governance review_item per ADR-review-items-primitive
- Advisor
/clients/:idrisk-profile detail view + bucket-override modal + sentinel-row chain visualization (pw-os-v2) - 7 client assessment screens + 9-state client-side state machine + scored-pending polling + re-assessment invitation flow (pw-portal-v2)
- ADRs + compliance policy + runbook at pw-shared (cli-shared #229)
- CCO REVIEW + APPROVAL records at compliance/cco-approvals/ — Rule 204-2(a)(7) canonical sign-off
Sprint metrics + §17 SELF-FIX BOUNDARY validations
- 1h 39min cli1 wall-clock for 3 sequential PRs (
#247+#248+ #249) — new sprint-velocity record on sequential-tasks-by-single-worker - 143 new tests in pw-api (1229 → 1372)
- 10 Phase 1.5 picks via AskUserQuestion across cli1 Tasks 1+2+3
- clients-merge.spec.ts:368 flake hit 2 of 3 PRs; both rerun-recovered cleanly (admin-override-aware Phase 6 100% success rate)
- §17 SELF-FIX BOUNDARY validations:
- cli1 caught + fixed
objection_window_seconds=72hfor review_items negation_default constraint violation (Task 3 self-discovery via own integration test) - cli1 caught + fixed
assessment_type='risk_profile'enum validation (same) - cli1 surfaced latent aml-review-item.ts bug (flagged for separate fix; correct §17 boundary held)
- cli1 caught + fixed
- Edit-before-git-mv pitfall validated TWICE this iteration — cli-shared #230 + cli1 PR
#234/#235; both required follow-up commit. Strong codification signal for next iteration close.
Six new memory anchors codified (this iteration end-to-end including earlier-day work)
aml-vendor-canonical-scorechain(canonical state anchor; Chainalysis moved off 2026-05-01)workers-re-anchor-origin-main-before-archive(codification candidate for ritual#19from cli2 PR#218squash-include)subagent-watchdog-prefer-fresh-cli-for-substantive-dispatch(subagent fit pattern; START-IMMEDIATELY mitigation parent memory)orchestrator-pr-merge-standing-authorization(operator durable instruction; orchestrator merges on green CI)- NEW
adam-cco-batched-routing-canonical(17-pick batched routing pattern; ~10x operator efficiency) - NEW
start-immediately-subagent-pattern-canonical(validated 3x; canonical for substantive doc-authoring subagent class)
Five codification candidates queued for next iteration close
- Edit-before-git-mv pitfall —
git mvafter Edit stages HEAD-blob; required follow-up commit (validated 2x: cli-shared + cli1) - check-pii-tags-drift.sh raw URL 404s on private repos — gh api workaround canonical
- Worker-launch-ritual archive default to PR-route (not direct push) — classifier enforces; ritual should match
- Simple
gh pr viewpoll suffices for same-iteration substrate gates — simpler than polling-loop v2 filesystem-signal pattern workers-re-anchor-origin-main-before-archive— codification candidate from Component 2 cli2 PR#218(carry-forward)
Component 5 custodian-data scope landed (subsequent iteration's Tier 0)
PR #228 lands strategy/pwos-onboarding-component-5-custodian-data-scope-2026-05-18.md (406 lines). Quiltt PRIMARY for TradFi account aggregation (existing substrate at pw-api/src/lib/quiltt-*.ts) + Schwab direct-API SCAFFOLDING (OAuth client + smoke test; production deferred v1.5); 6 canonical actions; 8 open questions for CCO; 4-stream dispatch shape pre-staged. Authored via Agent subagent (3rd START-IMMEDIATELY validation; ~5 min wall-clock).
Operator-cost calibration
~10 operator messages for the sprint (~2 launch-paste + ~5 progress-relay + ~2 approve-routine + 1 PR-227-merge-authorization) → 12 PRs landed = ~1.2 substantive surfaces per operator-coordination touchpoint. Best velocity-per-message ratio on record. Combined day total: 32 PRs across estate for ~30 operator messages = ~1 surface per message.
Sprint retro folded here
What worked: orchestrator-direct + Agent subagent + worker-fresh-CLI hybrid at sprint scale; CCO 17-pick batched routing validated; START-IMMEDIATELY subagent pattern validated 3x; §17 SELF-FIX BOUNDARY held cleanly even on substrate-fundamental enum + constraint mismatches; 100% admin-override-aware Phase 6 flake recovery; cli1 ran 3 sequential PRs in <2 hours.
What surprised: edit-before-git-mv pitfall validated TWICE in same iteration — strong signal for next-iteration ritual codification. cli1 surfaced 2 own-task constraint mismatches + 1 latent aml-review-item.ts bug via own integration tests — pattern that integration tests catch dispatch-substrate mismatches reliably.
What's queued: Component 4 e-signature IMPLEMENTATION dispatch (Tier 0 next iteration); 5 codification candidates → worker-launch-ritual #19-#22 OR memory entries; 2 CCO TODOs (Pick 11 HITL rules + Pick 16 ADV 2A language); latent aml-review-item.ts bug fix.
2026-05-18 (Component 2 KYC sprint — 14 PRs across 4-stream parallel fanout + AML vendor canonical correction + 4 new memory anchors + Component 3 scope authored)
Sprint window ~15:35–18:43Z UTC; ~3 hours wall-clock end-to-end; 14 PRs landed across pw-shared + pw-api + pw-os-v2 + pw-portal-v2 + Component 3 risk-tolerance scope doc. Component 2 KYC verification substrate substantively complete (kyc_sessions table + Veriff + Persona shadow + Scorechain AML + advisor dashboard + client portal screens + ADRs + runbook). CCO approved all 6 Component 2 picks (PR #214; ~30min turnaround); AML vendor canonical correction (Chainalysis → Scorechain two-layer) handled mid-sprint without derailing. Component 3 risk-tolerance scope doc authored via Agent subagent with START-IMMEDIATELY mitigation (PR #223; validated the watchdog-evading pattern).
Substantive PRs by stream
| PR | Repo | Title | Stream | Merged | LOC |
|---|---|---|---|---|---|
#216 |
pw-shared | docs(component-2-kyc): ADRs + DD placeholders + KYC runbook + ADR-webhook-receiver promoted ACCEPTED | cli-shared | 17:06:02Z | 5 new artifacts + 1 modified |
#244 |
pw-api | feat(kyc): kyc_sessions substrate + 14 canonical actions + PII_TAGS + sentinel-row reconciliation (PR-KYC-A) | cli1 Task 1 | 17:10:31Z | +1473/-1 |
#349 |
pw-os-v2 | feat(kyc): advisor /clients/kyc dashboard + per-client KYC tab + bulk action BFF routes + PII_TAGS port | cli2 | 17:18:35Z | 1211 tests pass |
#66 |
pw-portal-v2 | feat(kyc): 7 portal screens + Veriff JS SDK + state-machine UI + BFF routes + PII_TAGS port | cli3 | 17:32:20Z | +1750/-2 |
#245 |
pw-api | feat(kyc): Veriff client + webhook handler + Persona shadow-pilot (PR-KYC-B) | cli1 Task 2 | 18:00:11Z | +1275/-0 |
#220 |
pw-shared | fix(component-2-kyc): canonicalize AML vendor — Scorechain two-layer (Chainalysis Free OFAC discontinuing) | orchestrator-direct PR-fix-A | 18:29:01Z | +273/-287 |
#246 |
pw-api | feat(kyc): AML adapter + needs_review→review_item + clients-merge DEPENDENT_TABLES fix (PR-KYC-C) | cli1 Task 3 | 18:27:40Z | +781/-0 |
#222 |
pw-shared | docs(cco-approvals): AML vendor canonical clarification — Chainalysis → Scorechain | orchestrator-direct PR-fix-B | 18:42:58Z | +44/-0 |
#223 |
pw-shared | docs(component-3-risk-tolerance): scope doc DRAFT | Agent subagent (validates START-IMMEDIATELY) | 18:42:54Z | 304 lines new |
Dispatch + orchestration PRs (pw-shared)
PR #213 (4-stream dispatch pending.md) + #214 (CCO routing request — CCO approved) + #217 (cli-shared archive) + #218 (cli2 archive — incidentally squash-included PW-COMPONENT-2-KYC-CCO-APPROVAL doc) + #219 (cli3 archive) + #221 (cli1 archive — 3 tasks bundled; orchestrator re-archived after cli1's archive landed orphan on local fix/ branch).
Component 2 capabilities now live on main
- kyc_sessions substrate at
pw-api/src/db/schema/kyc-sessions.ts+ migration 0049_kyc_sessions.sql + migration 0050_kyc_sessions_relax_client_id_immutability.sql (immutability relaxed to permit clients-merge UPDATE path); 17a-4 + BSA 7-year retention; BEFORE-UPDATE immutability trigger; sentinel-row reconciliation per ADR-gcs-worm R3 day-one - 14 canonical PW_ACTIONS at
pw-api/src/lib/audit-actions.ts— 6kyc.session.*substrate-level + 6onboarding.kyc.*lifecycle + 2kyc.session.retry_*sentinel pair - PII_TAGS.kyc_sessions canonical at pw-api; byte-equal vendored in pw-os-v2 + pw-portal-v2 per ADR-PII-tagging R3 §4 (check-pii-tags-drift.sh enforced)
- Veriff integration: client wrapper + webhook handler (consumes Track B' webhook-receiver primitive; first production consumer; ADR promoted DRAFT → ACCEPTED) + Persona shadow-pilot logging
- AML adapter at
pw-api/src/lib/aml/component-2.tswraps existingkyw.tsScorechain orchestrator (Scorechain Free Sanctions API direct for OFAC + Scorechain Risk Assessment via QuickNode for KYT) - HALT-AND-QUEUE diagnostic closed — kyc_sessions added to clients-merge DEPENDENT_TABLES + migration 0050 relaxes immutability for merge UPDATE path
- Advisor
/clients/kycdashboard + per-client KYC tab + 3 BFF routes (pw-os-v2) - 7 client portal screens + Veriff JS SDK CDN dynamic loader + 9-state client-side state machine + 3 BFF routes (pw-portal-v2)
- ADRs landed: ADR-kyc-state-machine.md (Rev 1 → Rev 2 vendor correction) + ADR-aml-decisioning.md (Rev 1 → Rev 2 Scorechain canonical) + ADR-webhook-receiver-primitive.md (DRAFT → ACCEPTED) + ADR-gcs-worm-audit-mirror.md Rev 3 §"Applicability to future immutable tables" honored
- Operational runbook at
runbooks/kyc-verification.md(Rev 2 Scorechain canonical) - CCO records: PW-COMPONENT-2-KYC-CCO-REVIEW + PW-COMPONENT-2-KYC-CCO-APPROVAL (6 picks all APPROVED 2026-05-18T15:45:14Z via PR
#214; AML vendor canonical clarification appended via PR #222) - Scorechain vendor DD placeholder (replaces deleted chainalysis-due-diligence.md; full DD next iteration)
Three pattern observations + 4 new memories codified
AML vendor canonical correction handled cleanly mid-sprint. Scope doc cited Chainalysis "per memory" referring to stale state; canonical Scorechain pre-existed at
docs/compliance/kyc-aml-policy.md§6 +architecture/api/scorechain.md+pw-api/src/lib/kyw.ts(Chainalysis moved off 2026-05-01 — their free Sanctions API winding down). cli1 grep against pw-api code surfaced the drift; operator + CCO confirmed canonical at 18:00Z; PR-fix-A bundled 6 doc revisions; cli1 self-corrected mid-Task-3 to wrap existing kyw.ts instead of building new Chainalysis client. Memoryaml-vendor-canonical-scorechainanchors the canonical state to prevent future drift.§17 SELF-FIX BOUNDARY validated at scale. cli1 self-corrected 3+ dispatch oversights in one session without escalating: migration number drift (dispatch said 0047, cli1 used 0049); sentinel-action 3-segment regex compliance (dispatch's 4-segment → corrected to 3-segment); tenant_id + RLS added per codebase invariant; DEPENDENT_TABLES fix bundled into PR-KYC-C; mid-task Chainalysis→Scorechain pivot. Pattern: workers can self-correct dispatch-level oversights at code-author time without escalating when scope is bounded + correction is structurally honest.
START-IMMEDIATELY subagent mitigation VALIDATED. Component 3 risk-tolerance scope authored via Agent subagent with
START IMMEDIATELY: use TodoWrite within 30 secondslead-in + bounded scope + reference-by-path (not in-line ADR text). Subagent streamed TodoWrite ~10s into prompt; ran ~3 min wall-clock; landed PR#223(304 lines) clean. Companion failure (cli-shared earlier in iteration) without START-IMMEDIATELY directive watchdog-killed at 600s. Memorysubagent-watchdog-prefer-fresh-cli-for-substantive-dispatchupdated with validation.
4 new memories codified this iteration:
aml-vendor-canonical-scorechain(canonical state anchor; Chainalysis moved off 2026-05-01; Scorechain two-layer canonical)workers-re-anchor-origin-main-before-archive(codification candidate for worker-launch-ritual#19; cli2 PR#218squash-include precedent)subagent-watchdog-prefer-fresh-cli-for-substantive-dispatch(subagent fit pattern + START-IMMEDIATELY mitigation)orchestrator-pr-merge-standing-authorization(operator durable instruction: orchestrator merges PRs when CI/CD green + tests pass)
Five-stream + orchestrator-direct coordination held cleanly
- §15 multi-stream parallel fanout at 4-stream worker scale + orchestrator-direct + Agent subagent; cross-stream polling gate (cli2 + cli3 polled for cli1 PR-KYC-A merge); both unblocked within 7-min polling budget; Agent subagent for Component 3 scope ran in background while orchestrator authored iteration-close hygiene + PR-fix-A
- §16 per-worker worktree held cleanly across all 4 worker streams + the Agent subagent's isolation=worktree (auto-managed by Agent tool)
- §17 SELF-FIX BOUNDARY held + validated at scale (pattern observation #2)
- §20 fetch-before-grep applied: every cross-repo claim verified via
git fetch origin - Ritual
#9classifier denial held: cli2 hitgit pull origin mainclassifier denial → cli2 surfaced + self-resolved after operator authorization; orchestrator hit classifier denial on cco-approvals/ PR merge (PR #222) → held + operator granted standing authorization for future routine merges; orchestrator hit classifier denial on parallel-Bash with PR merge → cancelled the bundled subagent spawn + re-spawned subagent solo - Ritual
#18AskUserQuestion delivered ~22 picks total across the iteration at ~30s decision latency each (~10-12 min total pick latency vs 3h wall-clock)
Component 3 risk-tolerance scope authored (next-iteration Tier 0 unblock)
PR #223 lands strategy/pwos-onboarding-component-3-risk-tolerance-scope-2026-05-18.md (304 lines) — in-house FRTS-13 + PW overlay v1 questionnaire with Riskalyze deferred to v2; 1-100 composite score → conservative/moderate/aggressive bucket; risk_tolerance_sessions state machine mirroring kyc_sessions; sentinel-row reconciliation day-one per ADR-gcs-worm R3; 6 canonical actions; 3-year re-assessment cadence baseline; HITL Tier 2 on outlier responses; 6-year retention per Rule 204-2(a)(8); 4-stream dispatch shape pre-staged; 8 open questions queued for CCO. Authored via Agent subagent in ~3 minutes.
Operator-cost calibration
~25-30 operator messages for the sprint → 14 PRs landed = ~2 substantive surfaces per operator-coordination touchpoint. Excellent tier per substrate-patterns rubric Category 8. New sprint-velocity record on the 4-stream-parallel pattern + orchestrator-direct + Agent subagent hybrid.
Deferred follow-up + carry-forward
- 5 integration tests deferred (cli1 PR-KYC-C) — expected-verified + needs-review + AML-hit-escalation + Persona-divergence + Veriff-down failover paths; require real staging Veriff sandbox + ephemeral PG
- 9 + 8 CCO open questions in ADRs — 9 from Component 2 (risk-score threshold; PEP escalation order; Scorechain plan tier trigger; ongoing-monitoring cadence; state-specific obligations; manual review SLA; Persona day-30/90 review; v2 on-chain proof-of-ownership; multi-jurisdiction KYC) + 8 from Component 3 (re-assessment cadence; HITL threshold; question-bank source; score-to-bucket thresholds; advisor override authority; reg citation verification; ADV 2A Item 8 integration; cross-bucket override gate). Bundle with next CCO touchpoint.
- clients-merge.spec.ts:368 flake observed twice (PR-KYC-B + PR-KYC-C); rerun-retry resolved; tracked separately
- Component 3 IMPLEMENTATION dispatch — pre-staged at next-iteration-briefing.md; Tier 0 next iteration
Sprint retro folded here
What worked: multi-stream parallel fanout at 4 streams + orchestrator-direct + Agent subagent hybrid; CCO turnaround in <10 min via canonical REVIEW/APPROVAL doc-PR pattern; AML vendor canonical correction handled mid-sprint without derailing; START-IMMEDIATELY subagent mitigation validated for substantive doc authoring; §17 SELF-FIX BOUNDARY held cleanly under scale.
What surprised: scope doc's "per memory" Chainalysis citation propagated through briefing → CCO review → ADRs before cli1's grep caught it; memory anchors added to prevent future propagation. Subagent watchdog killed substantive dispatch (cli-shared) without START-IMMEDIATELY; with START-IMMEDIATELY, same scope class (Component 3) ran clean. Pattern is task-specific not class-specific.
What's queued: Component 3 risk-tolerance IMPLEMENTATION dispatch (Tier 0 next iteration); Component 4 e-signature scope authoring; Reg S-P PDFs awaiting CCO DocuSign (June 3 deadline carry-forward).
2026-05-18 (compliance hardening sprint — 6-PR audit-finding closure across 4 worker streams + §12.3 sentinel-row codification + AskUserQuestion canonical Phase 1.5 delivery)
Sprint window 13:15–14:30Z UTC; ~130 min wall-clock end-to-end; 6 substantive PRs + 8 dispatch infrastructure PRs landed across pw-shared + pw-api + pw-os-v2 + pw-portal-v2. Closes audit-2026-05-17.md (PR #202) HIGH findings T1.1 + T1.2 + T1.3 + T1.4 + T1.5 + 2 MEDIUMs + 2 unwired-guard gaps. Hardened compliance baseline ready for Component 2 KYC dispatch on next iteration.
Substantive PRs by stream
| PR | Repo | Title | Stream | Merged | LOC |
|---|---|---|---|---|---|
#204 |
pw-shared | feat(governance): canonical PII_TAGS drift script + ADR-PII-tagging §4 + ADR-gcs-worm Revision 2 + rule-17a-4 evidence-trail update | cli-shared (PR-E) | 13:15:41Z | +266/-1 |
#240 |
pw-api | feat(pii-tags): Turnkey + onchain client-financial coverage (audit T1.3) | cli1 Task 1 (PR-A1) | 13:35:55Z | (admin-override) |
#347 |
pw-os-v2 | feat(pii+observability): port pw-api PII_TAGS canonical + middleware honest-relabel (audit T1.2 + T2) | cli2 (PR-C) | 13:47:41Z | +941/-193 |
#63 |
pw-portal-v2 | feat(pii+observability): port + relabel + requestIdMiddleware (audit T1.2 + T2 + T1.4) | cli3 (PR-D) | 13:51:49Z | +1370/-177 |
#242 |
pw-api | feat(audit): canonical-action guard + actor_email required + missing PW_ACTIONS (audit T1.5 + MEDIUMs) | cli1 Task 2 (PR-A2) | 14:04:07Z | +194/-21 |
#243 |
pw-api | feat(audit): WORM mirror broad audit_log coverage (audit T1.1) — §12.3 sentinel-row pivot | cli1 Task 3 (PR-B) | 14:28:36Z | +722/-20 |
Dispatch infrastructure PRs (pw-shared)
PR #203 (4 pending.md dispatch inbox) + #205 (cli-shared archive) + #206 (cli1 failed-ci archive — predecessor) + #207 (cli1 continuation pending.md) + #208 (cli2 archive) + #209 (cli3 archive) + #211 (cli1 continuation archive).
Audit findings closed
- T1.1 GCS WORM mirror extended to broad audit_log coverage —
writeAuditMirror({eventType: 'audit_log', ...})invoked from insidewriteAuditLog()post-INSERT; every audit_log row mirrors togs://pwllc-audit-archiveregardless of caller; recursion guard prevents sentinel-row infinite loops; reconciliation cron daily 02:00 UTC; fail-soft posture preserves audit_log INSERT semantics on transient GCS failure - T1.2 PII_TAGS canonical cross-repo alignment — pw-os-v2 + pw-portal-v2 ported byte-aligned to pw-api canonical (post-PR-A1); local-fixture sync test retired in favor of
shared/scripts/check-pii-tags-drift.shcanonical + vendored-copy SHA-256 self-canary in each BFF CI - T1.3 Turnkey + onchain PII_TAGS coverage —
client_turnkey_bindings+client_turnkey_recovery_events+ 11 onchain client-financial tables added - T1.4 Request-id propagation —
requestIdMiddlewareadded to pw-portal-v2;x-request-idforwarded bypwApiPost+lookupPortalByEmail+chat-tools.callPwApi; portal-originated audit_log rows no longer haverequest_id = null - T1.5 Actor-attestation completeness —
actor_emailrequired across/v1/internal/*mutation routes;isCanonicalAction()helper invoked at top ofwriteAuditLog()(THROW dev/test; WARN-loud prod); 9 inline-action strings replaced with PW_ACTIONS constants (5 audit-cited + 2 governance-additional + 2 strict-3-segment renames); parseAction + verb-regex aligned (3-segment canonical) - T2 unwired pii-prompt-construction middleware — both BFFs found ZERO row-shaped SDK call sites needing wiring; correctly relabeled file headers to honest-report substrate-awaiting-consumer state rather than wiring fictional consumers (audit's framing assumed code-wiring was the fix; truth was header-correction was the structurally honest fix)
- MEDIUMs —
governance.action.taken+governance.vote.castinline → PW_ACTIONS constants;actor_emailZod-optional inconsistency resolved
Three pattern observations worth codifying
§12.3 good-stop fired live on cli1 PR-B. Phase 1 baseline surfaced
audit_log is DB-immutable (BEFORE UPDATE trigger raises)— a constraint the dispatch didn't pre-stage in its Phase 1.5 picks. cli1 correctly escalated via AskUserQuestion; operator picked Approach C (insert-time-only + sentinel rows). Pattern: when Phase 1 baseline surfaces a premise-invalidating constraint mid-Phase, escalate to Phase 1.5 STOP even if dispatch didn't pre-stage that exact pick. Memory candidate:phase-1-5-stop-retroactive-escalation.Sentinel-row pattern is canonical reconciliation shape for WORM/immutable tables. Reconciliation MUST NOT UPDATE failed rows on tables with BEFORE-UPDATE-triggers (audit_log, review_actions, ai_runs, future kyc_verifications, future signed_document_archive). Instead, INSERT a NEW row (sentinel) referencing the failed row's ID + retry attempt + outcome. Preserves immutability invariant + still enables defense-in-depth coverage of mirror-write failures. Documented at ADR-gcs-worm-audit-mirror.md Revision 3. Memory candidate:
sentinel-row-immutable-reconciliation-pattern.AskUserQuestion is the canonical Phase 1.5 STOP delivery mechanism. Workers used AskUserQuestion across 5 hardening-sprint Phase 1.5 stops (cli-shared 5 picks + cli2 4 picks + cli3 3 picks + cli1 Task 2 3 picks + cli1 Task 3 4 picks). ~30s decision latency per pick; ~20 pick resolutions for 6 PRs at ~10 min total pick latency vs ~130 min sprint wall-clock. Codified as worker-launch-ritual item
#18. Memory candidate:askuserquestion-phase-1-5-canonical-delivery.
Five-stream coordination via §15-§21 ritual
- §15 multi-stream parallel fanout validated at 4-stream scale (cli1 + cli2 + cli3 + cli-shared) with cross-stream polling-loop gate (cli2 + cli3 polled for cli1 PR-A1 merge via
gh pr list --searchpattern match; both unblocked at 13:36Z within polling-budget) - §16 per-worker worktree held cleanly across all 4 streams; 4 worktrees created + cleaned up post-Phase-7
- §17 SELF-FIX BOUNDARY held: cli2 + cli3 correctly relabeled file headers rather than wiring fictional consumers (honest-report acceptance); cli1 surfaced architectural constraint via §12.3 rather than silently absorbing
- §20 fetch-before-grep applied: every cross-repo claim (pw-api canonical reads from pw-os-v2 + pw-portal-v2 ports) refreshed via
git fetch origin - §21 case-naming awareness applied: cli2 + cli3 surveyed jq output structure before scripting filters
Two pre-existing main CI failures handled
- pw-api Migration dry-run RED on
clients-merge.spec.ts:368(pre-existing from PR #239); blocked cli1's PR-A1 auto-merge; operator force-merged via admin-override at 13:35:55Z. cli1 continuation pending.md added admin-override-aware Phase 6 protocol; PR-A2 + PR-B subsequently auto-merged cleanly (failure was specific to PR#239content, not Tasks 2+3 paths). Tracked separately for follow-up resolution. - pw-portal-v2 self-canary trailing-newline bug introduced by PR-D
#63itself (cli3 surfaced + correctly left out-of-scope); filed as shared/#210; ~10 LOC mechanical fix tracked ascompliance-hardening-tier-1-2-2026-05-18-followupfast-follow (pw-os-v2 + pw-portal-v2 CI workflow edits in flight via orchestrator-internal Task subagents this iteration).
Operator-cost calibration
~30 operator messages for the sprint (~5 launch-paste + ~20 pick-resolution + ~5 admin-override + miscellaneous) → ~5 substantive outcomes per operator message. "Excellent <15-message-block" tier per substrate-patterns rubric Category 8.
Sprint retro folded here (no separate doc per operator directive)
What worked: multi-stream parallel fanout at 4 streams; AskUserQuestion tightened pick-resolution latency vs chat-narration; ritual #15 PR-route fallback fired correctly twice (cli-shared + cli3 archives); §17 honest-report acceptance held cleanly across all worker dispatches.
What surprised: audit drift estimates were conservative (cli2 + cli3 baseline diffs both exceeded ±5 gate); the T2 "wire middleware" finding turned out to be a header-correction not code-wiring — workers discovered via Phase 1 enumeration; Component 2 KYC compliance baseline now substantially stronger than the dispatch authored against.
What's queued: fast-follow #210 self-canary fix (in flight); Component 2 KYC dispatch (gated on operator return + kyc_sessions-vs-kyc_verifications reconciliation decision).
2026-05-17 (post-dogfood vendor-DD update — Anthropic ZDR verified evidence + Trust Portal references + CCO review_item bootstrap queued)
First substrate dogfood loop closed 2026-05-17 ~18:37Z with ai_run 3bfd0f73-4297-4dc6-9789-60d6a4ea6c69 (upstream msg_0167ULtBanVNFiDiNqz6GpDZ) — fail-closed ZDR validation at pw-api/src/governance/claude-client.ts:108 exercised end-to-end; ANTHROPIC_ZDR_CONFIRMED=true bound on pw-api Cloud Run revision pw-api-00233-lwc (us-central1); ai_runs.zdr_confirmed CHECK constraint held; cost 0.2591 USD; 1321 in / 3190 out tokens; 44.169s elapsed; 11 flagged_issues at confidence 55. Vendor DD doc moved from DRAFT (placeholder-pending) to ACTIVE v1.1 reflecting verified operational evidence rather than aspirational language.
pw-shared (THIS PR) — vendor-DD verified ZDR update.
compliance/vendor-correspondence/anthropic-due-diligence.mdv1.1 ACTIVE: replaced all_pending CCO confirmation_placeholders with verified evidence — GTM contact Pascale Richards [email protected]; notice email 2026-04-21 12:04 AM ("Protocol Wealth LLC ZDR" — verbatim quote retained); effective date 2026-04-23 (1-2 business days post-notice); console verification at https://platform.claude.com/settings/privacy on ~2026-04-23 + re-verification 2026-05-17 ~18:30Z confirming 0-day retention + US Inference Geo + US Workspace Geo (immutable) + user feedback DISABLED + Claude Code metrics logging DISABLED. New sections: "ZDR enrollment — verified evidence" + "Operational substrate-side attestation" (Cloud Run env binding + fail-closed validation + DB CHECK constraint defense-in-depth + first ai_run identifiers) + "External compliance artifacts (Anthropic Trust Portal)" (SOC 2 / ISO 27001 / ISO 42001 / GDPR DPA / Subprocessor list / pen-test refs; retention plan atgs://pwllc-audit-archive/vendor-dd/anthropic/reserved; review cadence). Approval table: CTO/CISO signed 2026-05-17 (operator authority); CCO pending via substrate-mediatedcompliance_docreview_item (companion pw-api migration). Annual review cadence anchored to ZDR effective date (next 2027-04-23).compliance/vendor-correspondence/evidence/directory scaffolded (.gitkeep+README.mdcovering naming convention + retention posture + PII discipline). Operator commits actual screenshots (anthropic-zdr-notice-pascale-richards-2026-04-21.png+anthropic-console-privacy-2026-05-17.png+anthropic-console-security-geo-2026-05-17.png) separately.runbooks/governance-review-primitive.mdupdated — Manual Verification Path Step 2 now cross-references vendor-DD verified evidence + canonical first ai_run identifier; ZDR Contract Failure recovery path names console verification URL + the canonical vendor-DD doc.Companion pw-api migration queued (PR 2). Bootstrap
compliance_docreview_item inreview_itemstable — surfaces CCO acknowledgment via the substrate-mediated governance flow rather than out-of-band email. Migration depends on this PR's merged content for SHA-256content_hashcomputation; opens after this PR lands.
2026-05-16 (evening-block FINAL-ANCHOR close — full evening arc: PR-Z4-portal + lifecycle: v2 flip + Wealthbox inbound sync + Gate 2 + Gate 3 + design doc + design doc revisions; substrate essentially complete; bridge-work design landed; STAND DOWN to tomorrow morning's Track 0 partner-prep)
Block 3 of 2026-05-16 same-day continuation iteration. ~2.5-hour wall-clock window (21:45–24:00Z UTC). ~12 PRs across the evening block (pw-shared #178/#179/#180/#181/#182/#183/#184 + pw-api #222 + pw-portal-v2 #60 + Gates 2+3 + design doc revisions). Substrate essentially complete after Wealthbox inbound sync (PR #222) lands; bridge-to-product mode transitioned at block close; design doc + 6 revisions covering 8 MVP components landed. Tomorrow morning's iteration opens with Track 0 partner-prep deliverables (1-page visual summary + CCO questionnaire + CIO questionnaire) ahead of Phase 1 engineering dispatches.
pw-portal-v2
#60MERGED 2026-05-16T21:57:18Z — PR-Z4-portal middleware port (subagent dispatch; mirror of pw-os-v2#336actual landed scope). Subagent's Phase 1 diagnostic surfaced briefing-overstated-PR-scope finding; codified as new memorybriefing-summaries-may-overstate-pr-scope.md(Gate 3 mutation).pw-shared #178 MERGED 21:55:34Z — bundled dispatch+lifecycle PR (orchestrator-direct). Four surfaces: cli2 Wealthbox inbound sync pending.md + lifecycle.md §9 Q1+Q6 settlement (per-worker git-worktree +
awaiting: operator-decisioncanonical) + worker-launch-ritual.md#7cross-reference + orphan-branch safety doc.pw-shared #179 MERGED 22:01:02Z — substrate-patterns 8-category rubric (
strategy/substrate-patterns-2026-05-16.md; 171 lines).pw-shared #180 MERGED 22:17:22Z — cli2 wealthbox-inbound-sync claim + Phase 1.5 SURFACE archive. Operator picked 1d/2a/3b/4a (manual-only / embedded API path / dual-write / malformed-only audit emit); all 4 matched orchestrator pre-stage recommendations.
pw-api
#222MERGED 22:55:43Z — Wealthbox inbound NPI sync producer-side path (cli2 dispatch). 372 LOCsrc/lib/wealthbox-npi-sync.ts+ 82 LOC route handler + 397 LOC tests. Consumes PR#221skeleton end-to-end: discrete columns + canonical PII_TAGS extension + audit-action constantWEALTHBOX_NPI_BACKFILL_MALFORMEDall exercised by producer-side path. Dual-writes to discrete columns +clients.metadatakeys during migration window.pw-shared #181 MERGED 22:58:20Z — cli2 wealthbox-inbound-sync completed archive.
Gate 2 — 23 shared/ remote orphan branch batch cleanup EXECUTED 22:02Z (operator-authorized binary go). 21 explicit deletes + 2 already-auto-deleted between safety-list-capture and execution (PR
#176/#177source branches;--delete-branchlineage). 23/23 target branches absent; remote pw-shared has exactly 1 branch (main).Gate 3 — 2 memory mutations applied 22:05Z. (1) NEW memory
briefing-summaries-may-overstate-pr-scope.md(gh pr view --json files verification discipline). (2) UPDATE memorylifecycle-v2-rollout-3-of-3-pattern.mdwith 6-consecutive-clean-dispatches-post-flip evidence (3 opt-in + 3 post-flip). MEMORY.md index entry added./tmp/dream-pass-output-2026-05-16.mdarchived post-application.pw-shared #182 MERGED 23:11:02Z — evening-block CHANGELOG entry (this entry's predecessor; intermediate-state capture).
pw-shared #183 MERGED 23:19:35Z — PWOS onboarding scope design doc (
strategy/pwos-onboarding-scope-2026-05-16.md; 483 lines; 7 sections covering substrate-to-onboarding mapping + 7 MVP components + Anvil sub-tasks + sequencing + ASAN frame + CCO gates + timeline). Substrate inventory subagent (background; ~5 min cycle) provided capability inventory; orchestrator-direct synthesis layered worker-cycle estimates + CCO gate batching guidance.pw-shared #184 (THIS PR) — design doc revisions + final-anchor CHANGELOG + CURRENT-STATE refresh + briefing rewrite. 6 design doc revisions per operator directive: (1) Sub-task C7 webhook-receiver primitive ADR pointer (Phase 1 Track B' first deliverable; status DRAFT pending CTO/CISO review tomorrow); (2) Component 4 e-delivery consent acceptance criterion +
compliance.disclosure.e_delivery_consentedcanonical action; (3) Component 5 custodian architecture inversion (v1 manual handoff at Schwab/Altruist/IBKR's own flows + custodian-account-number capture + Quiltt sync activation; Altruist/IBKR API account-opening deferred to v2; ~3-4 cycles → ~1-2 cycles); (4) Component 8 IPS generation added (CFA-Institute-style sections + CIO methodology gate + 4 new canonical actions; total registration up to ~33 actions); (5) PW-direct ACH alternative deferred to v2 backlog (one-sentence Component 6 note); (6) Section (a) substrate map updated to surface Component 8 + Wealthbox NPI inbound sync use cases. Timeline tightened 4-8 → 4-7 weeks per custodian inversion.Substrate validation through evening block. 6 consecutive clean lifecycle: v2 dispatches now stretches to 7 (PR-Z2 + PR-Z3 + PR-D1 opt-in + PR-Z4 + Wealthbox NPI
#221+ PR-Z4-portal pw-portal-v2#60+ Wealthbox inbound sync pw-api#222post-flip). Worker-extends-pre-stage pattern held across all 3 post-flip dispatches (Pick 7(b) canary-revert; Pick 0 architectural-premise reframe; PR-Z4-portal scope-correction). Per-dispatch operator-touchpoint cost held at 1-2 messages. Operator-cost calibration this evening block: ~12 operator messages for ~12 PRs + 2 gate-ops + 2 memory mutations + design doc + 6 revisions = ~1.3 substantive outcomes per operator message; firmly within "Excellent <15-message-block" tier per substrate-patterns rubric Category 8.Transition to bridge-to-product mode complete at block close. Substrate essentially complete after Wealthbox inbound sync (PR #222) lands. Bridge-work design landed (PR
#183+ revisions in #184). Tomorrow morning's iteration opens with Track 0 partner-prep deliverables ahead of Phase 1 implementation dispatches: 1-page visual summary of the 8-component onboarding flow + CCO questionnaire (gate sequencing prefs + copy review prefs + compliance posture confirmation) + CIO questionnaire (IPS methodology prefs + EMF integration framing + capital-structure handoff prefs). Output paths:shared/strategy/pwos-onboarding-partner-prep-2026-05-17.md+shared/strategy/partner-questionnaire-adam-2026-05-17.md+shared/strategy/partner-questionnaire-jason-2026-05-17.md. Subsequent Phase 1 Track B' (webhook-receiver primitive ADR; CTO/CISO review gate) + Track A (Component 1 portal auth) + Track C (~33 canonical-action registration) fire after Track 0 lands.Carry-forward to tomorrow's iteration: Track 0 partner-prep deliverables (orchestrator-direct + subagent); Phase 1 Track A/B'/C/D/D' engineering + compliance/methodology gate sequencing; 22 shared/ remote orphan branches → 0 (cleanup complete; no carry-forward); 2 memory mutations applied; lifecycle: v2 substrate at 7 consecutive clean dispatches.
2026-05-16 (evening-block completion — Wealthbox inbound sync producer-side path lands; substrate essentially complete; transition to bridge-to-product mode)
Same-day continuation third block (~2-hour window 21:45-23:00Z, ~1h after the iteration-close anchor PR #177). 7 PRs landed across pw-shared + pw-api + pw-portal-v2 + 2 orphan-cleanup gate operations + 2 memory mutations applied. Substrate essentially complete after PR #222 landing — Wealthbox inbound NPI sync makes the PR #221 skeleton useful; producer-side path enables the natural follow-on that previously only existed as scoping doc. Operator transitioned to bridge-to-product mode at block close.
pw-portal-v2
#60MERGED 2026-05-16T21:57:18Z — PR-Z4-portal middleware port. Subagent dispatch (worktree-isolation;feat/stream-z-pr-z4-portal-middleware). Ports pw-os-v2 PR-Z4 PII-tagging middleware to pw-portal-v2:apps/api/src/lib/pii-tags.ts(174 LOC canonical map mirror) +pii-prompt-construction.ts(422 LOC excludeHighPii middleware) +pii-tags-sync.test.ts(162 LOC drift detector) +audit-actions.ts(42 LOC minimal mirror). Anthropic egress inanthropic-client.ts(raw fetch, not SDK) deliberately untouched percanary-vs-classifier-route-conflictmemory; middleware lands as structural guard ready for future per-row callers (portfolio summarization, account narration). Subagent's Phase 1 diagnostic surfaced briefing-overstated-PR-scope finding: the iteration-close briefing described pw-os-v2#336as wrapping "5 SDK call sites in claude-client.ts," butgh pr view 336 --json filesshowed the actual landed scope was middleware infrastructure +broadcasts.tswrap +inbound-ai-classifier.tswrap (later reverted). Subagent correctly mirrored landed-scope not briefed-scope. New memory artifactbriefing-summaries-may-overstate-pr-scope.mdcodifies thegh pr view --json filesverification discipline for any port/mirror dispatch.pw-shared #178 MERGED 2026-05-16T21:55:34Z — bundled dispatch+lifecycle PR (orchestrator-direct). Four surfaces in one PR per CLAUDE.md §1.4 routine-doc-maintenance policy: (1)
dispatch/cli2/pending.mdWealthbox inbound sync producer-side path dispatch with 4 Phase 1.5 picks pre-staged; (2)dispatch/protocol/lifecycle.mdPhase 1 ADOPTION sub-items (b)+(c) settled — §9 Q1 codifies per-workergit worktreeas canonical structural fix retiring ritual#7in Phase 2; §3.2+§9 Q6 codifyawaiting: operator-decisionas canonical Phase 1.5 STOP enum value superseding the transitionaloperator-pickterm; (3)dispatch/shared/worker-launch-ritual.mditem#7cross-reference appended naming the canonical per-worker-worktree pattern; (4)strategy/orphan-branch-cleanup-batch-2026-05-16.mdsafety capture doc for the 23 shared/ remote orphan branches (all verified MERGED with --delete-branch lineage).pw-shared #179 MERGED 2026-05-16T22:01:02Z — substrate-patterns 8-category rubric durable artifact.
strategy/substrate-patterns-2026-05-16.md(171 lines). Persists the 8-category cross-iteration substrate-patterns rubric distilled from 2026-05-16 same-day continuation iteration close. Functions as canonical decision-making framework for orchestrator routing + dream-pass memory analysis. Categories cover multi-stream parallelization / worker-extends-pre-stage / ritual hardening compounding / lifecycle: v2 rollout pattern / CCO-CTO gate framing / substrate-questions-resolve-via-investigation / architecturally-exempt-needs-memory-artifacts / operator-cost calibration. Companion application output at/tmp/dream-pass-output-2026-05-16.md(per-iteration application; deleted post-Gate-3 mutations).pw-shared #180 MERGED 2026-05-16T22:17:22Z — cli2 wealthbox-inbound-sync claim + Phase 1.5 SURFACE archive. cli2 worker's interim archive of Phase 1.5 SURFACE state. cli2 surfaced 4 picks per dispatch + the operator picked
1d / 2a / 3b / 4a(manual-only / embedded API path / dual-write semantics / malformed-only audit emit). All 4 matched orchestrator pre-stage recommendations.pw-api
#222MERGED 2026-05-16T22:55:43Z — Wealthbox inbound NPI sync producer-side path. cli2 dispatch (post-flip lifecycle: v2 default-on). 372 LOC new modulesrc/lib/wealthbox-npi-sync.tsexportingsyncContactNpiFromWealthbox(contactId, deps)with Zod-validated cast-with-fallback per column type; 82 LOC new route handler atPOST /v1/internal/wealthbox/contacts/:id/sync-npi(system-tenant auth); 397 LOC tests covering happy-path + malformed-cast per column type + idempotency + Wealthbox response shape drift. EmitsWEALTHBOX_NPI_BACKFILL_MALFORMEDaudit-action on cast failure (constant already registered in PR#221skeleton). Dual-writes to discrete columns ANDclients.metadatakeys during migration window per design spec §3 step 2. PR#221skeleton now consumed end-to-end: discrete columns + canonical PII_TAGS map extension + audit-action constant all exercised by an actual producer-side path. Future operator-direct PR removes metadata writes after ~1 stable iteration confirms the discrete-column path.pw-shared #181 MERGED 2026-05-16T22:58:20Z — cli2 wealthbox-inbound-sync completed; archived. Terminal completion archive at
dispatch/cli2/done/2026-05-16-225727-completed.md.Gate 2 — 23 shared/ remote orphan branch batch cleanup EXECUTED 2026-05-16T22:02Z. Operator-authorized binary go (per memory
classifier-denial-on-bulk-destructiveper-action authorization). Loop executed: 21 explicit deletes succeeded; 2 reportedReference does not exist(PR#176/#177source branches; already auto-deleted between safety-list capture and execution via--delete-branchlineage). 23/23 target branches absent. Post-executiongit ls-remote origin 'refs/heads/*'minus main returns empty — remote pw-shared has exactly 1 branch (main). Memoryorphan-cleanup-must-exclude-open-pr-branchessafety check held cleanly (pre-executiongh pr list --state openreturned[]).Gate 3 — 2 memory mutations applied 2026-05-16T22:05Z. (1) NEW memory artifact
~/.claude/projects/-home-nick-projects-pw-shared/memory/briefing-summaries-may-overstate-pr-scope.md(55 lines) codifies thegh pr view <N> --json files,additions,deletionsverification pattern for any port/mirror/cross-repo-expansion dispatch — surfaced by PR-Z4-portal subagent's diagnostic correction of briefing's PR-Z4 framing. (2) UPDATE existing memorylifecycle-v2-rollout-3-of-3-pattern.mdextends with 6-consecutive-clean-dispatches-post-flip evidence section (PR-Z2 + PR-Z3 + PR-D1 opt-in + PR-Z4 + Wealthbox NPI#221+ PR-Z4-portal pw-portal-v2#60post-flip). Original 3-of-3 framing preserved as historical anchor; updated description names 3-opt-in-plus-3-post-flip substrate-scaling validation. MEMORY.md index entry added for the new artifact./tmp/dream-pass-output-2026-05-16.mdarchived (deleted post-application).Lifecycle: v2 substrate scaling validated at 6 consecutive clean dispatches. PR-Z2 + PR-Z3 + PR-D1 + PR-Z4 + Wealthbox NPI
#221+ PR-Z4-portal pw-portal-v2#60. The post-flip 3 dispatches confirm the substrate holds beyond the rollout window. Worker-extends-pre-stage pattern held cleanly across all 3 post-flip dispatches (Pick 7(b) canary-revert; Pick 0 architectural-premise reframe; PR-Z4-portal scope-correction). Per-dispatch operator-touchpoint cost held at 1-2 messages. Same-day continuation pattern viable — 3 of the 6 dispatches landed within a single 2-hour same-day continuation block; substrate handles rapid throughput without quality degradation.Transition to bridge-to-product mode at block close. Operator-directive: substrate essentially complete after Wealthbox inbound sync (PR #222) lands; natural next work is the bridge from substrate to client-facing utility. Formal iteration close (CURRENT-STATE refresh + briefing rewrite) deferred to end of bridge-work block. Next active work: PWOS onboarding scope design doc at
shared/strategy/pwos-onboarding-scope-2026-05-16.md— substrate-to-onboarding mapping + 7 MVP onboarding components + Anvil integration sub-tasks + sequencing recommendation + ASAN evaluation + CCO compliance gates + timeline. Tomorrow's iteration opens with FIRST implementation work that produces end-product utility — CTO/CISO + CCO + CIO use PWOS for actual client work within ~4-8 weeks of focused engineering.Operator-cost calibration. ~7 operator messages this block ([launch + Phase 1.5 picks operator response + Gates 2+3 authorization + PR
#222confirmation + transition directive]; within "Excellent <8" tier per substrate-patterns rubric Category 8). 7 PRs + 2 gate-ops + 2 memory mutations = 11 substantive outcomes per ~7 operator messages = ~1.6 outcomes per operator message. Calibrated pre-staging + worker-extends-pre-stage held the cost down through the block.
2026-05-16 (same-day continuation close — Stream Z FULLY CLOSED via PR-Z4 + Wealthbox NPI skeleton + lifecycle: v2 codification + Dependabot grooming + worktree hygiene)
Same-day continuation iteration opened ~1h after the prior iteration's 19:11Z stand-down. 7 PRs landed across pw-shared / pw-os-v2 / pw-api / pw-website + 1 PR closed + worktree+branch hygiene sweep + capacity-permitting Dependabot grooming. Stream Z FULLY CLOSED in production: PR-Z4 (pw-os-v2 middleware port) closes the structural-guard gap on pw-os-v2's claude-client.ts — the actual production Anthropic SDK call surface (5 egress sites). pw-os-v2 now has all 4 defense layers in production.
pw-os-v2
#336MERGED 2026-05-16T20:14:26Z — PR-Z4 port prompt-construction exclusion middleware. cli1 dispatch (first post-flip lifecycle: v2 default-on dispatch). Ports pw-api PR-Z3 reference implementation to pw-os-v2'sapps/api/src/lib/claude-client.ts. Operator picks at Phase 1.5: (1b) port canonicalPII_TAGStyped map + sync test; (2b) module-level wrap mirroring PR-Z3 structural shape; (3) ESLint custom rule extending pw-os-v2's existing config withno-restricted-syntaxselectors; (4c) deferPII_WAIVER_SECRETprovisioning to separate operator-direct terraform PR; (5) accept canonical audit-event names exactly. cli1 surfaced an additional Pick 7(b) at Phase 1.5: existing PR-Z3 middleware wrap ofinbound-ai-classifier.tswould trigger the egress canary on test fixtures with identifier-bearing sender emails (e.g.,[email protected]). Operator picked Pick 7(b) reverted-wrap pattern: keep broadcasts wrap, revert classifier wrap. Memory artifactcanary-vs-classifier-route-conflict.mdcaptures the conflict class for future PR-Z4-portal + similar wraps.pw-api
#221MERGED 2026-05-16T20:04:32Z — Wealthbox NPI promotion to discrete columns (skeleton). cli2 dispatch. Critical Phase 1 finding: design spec atstrategy/wealthbox-npi-fields-promotion-plan.md(PR #163) assumed inboundWealthbox custom_fields → clients.metadatasync that DOES NOT EXIST in pw-api today — Wealthbox integration is OUTBOUND-only (src/lib/wealthbox.ts+src/lib/dispatcher.tspush to Wealthbox;contactResponseSchema.passthrough()doesn't whitelist custom_fields; no code path reads custom_fields from GET responses or writes to clients.metadata). cli2 surfaced a NEW Pick 0 (architectural premise) before the 5 design-spec picks; operator picked Pick 0(a) ship structural skeleton only — 5 new typed columns + Drizzle mirror + canonicalPII_TAGSmap extension + audit-action constant + Zod schema-drift guards; NO backfill UPDATE (no source data) + NO ingest dual-write (no inbound ingest path). Documented deferred dual-write in PR body as follow-up gated on the future inbound sync PR. The skeleton has standalone value (downstream PRs write directly to discrete columns instead of metadata-blob) and avoids speculative implementation. Picks 1, 2, 5 stood as recommended; Picks 3 + 4 N/A under Pick 0(a).pw-shared #172 MERGED 2026-05-16T19:42:21Z — dispatch activations (cli1 PR-Z4 + cli2 Wealthbox NPI). Single PR carrying both worker pending.md files. PR-Z4:
git mvfromdispatch/shared/pr-z4-staged-dispatch.mdtodispatch/cli1/pending.md+ preamble strip +dispatched_at: 2026-05-16. Wealthbox NPI: drafted cli2 pending.md from 197-line scoping plan (PR #163) using canonical Phase 0-6 dispatch shape; 5 Phase 1.5 picks pre-staged with recommendations.pw-shared #173 MERGED 2026-05-16T19:52:38Z — lifecycle: v2 default-on flip codification. Decision 1 fired post-PR-D1 merge; 3 substrate docs updated:
dispatch/protocol/lifecycle.md(§1 status flip "Phase 1 design — opt-in" → "Phase 1 default-on; opt-in window CLOSED 2026-05-16; lifecycle: v2 canonical on all worker dispatches" + §8 migration path split: §8.1 historical opt-in reference + §8.2 default-on CURRENT with substrate validation evidence: state machine integrity / Phase 1.5 STOP discipline / frontmatter atomicity / operator-touchpoint cost + §9 open questions Q6/Q7/Q8 updated with observations from the 3-dispatch rollout window + footer restates substrate validation);strategy/dispatch-next-iteration-queue.md(Queue A row splits Phase 1 ADOPTION work into completed sub-item (a) + carry-forward sub-items (b)+(c); Phase 2 unblocked; Iteration handoff reframes opt-in narrative; Decisions 1/2/3/4 marked FIRED/SHIPPED/HOLD LIFTED/CLEARED);strategy/orchestrator-daily-start-prompt.md(foundational tier + time vocabulary + cold-start file list + worker dispatch step 1 frontmatter shape + polling-loop v2 footer).pw-shared #176 MERGED 2026-05-16T20:46:32Z — Dependabot grooming batch report. Subagent task (Agent tool with
isolation: worktree). 3 PRs in scope: pw-api#184(minor-and-patch group, 5 updates) → auto-merge ENABLED 20:44:16Z; pw-website #36 (@vitejs/plugin-react5→6, MAJOR) → recommend CLOSE (Astro 6 pins Vite 7; plugin 6 requires Vite 8 + removes Babel — upstream blocker); pw-website #53 (typescript5→6, MAJOR) → recommend DEFER (single mechanical tsconfig fix: add"ignoreDeprecations": "6.0"). Subagent operator-touchpoint: 1 message (post-completion summary review). Report atdispatch/cli1/done/2026-05-16-164449-dependabot-grooming-batch.md.pw-website #106 MERGED 2026-05-16T21:29:38Z — tsconfig.json
ignoreDeprecations: "6.0". Orchestrator-direct 1-commit mechanical fix per operator pick; suppressesbaseUrldeprecation warning that TypeScript 6.x would emit, unblocking Dependabot PR#53. Per memoryorchestrator-direct-threshold: trivial 1-line config edit qualifies as orchestrator-direct (dispatch overhead ≈ work itself).pw-website #53 MERGED 2026-05-16T21:32:50Z — typescript 5.9.3 → 6.0.3 (Dependabot major). Operator-approved DEFER-CLOSED path: tsconfig fix landed at
#106→@dependabot rebase→ CI green after rebase (test 22.x SUCCESS) → auto-merge fired immediately on green. Total cycle: ~3 min wall-clock from rebase trigger to merge.pw-website #36 CLOSED 2026-05-16T21:21 —
@vitejs/plugin-react5→6 (Dependabot major). Operator-approved CLOSE path per upstream dependency-graph conflict (Astro 6 pins Vite 7; plugin 6 requires Vite 8 + removes Babel). Dependabot will re-propose when Astro picks up Vite 8. Closing rationale in PR comment cross-references batch analysis atdispatch/cli1/done/2026-05-16-164449-dependabot-grooming-batch.md.pw-api
#184— Dependabot minor/patch group (auto-merge enabled). 8 CI checks all SUCCESS;autoMergeRequestenabled 20:44:16Z by subagent; mergeStateStatus: BEHIND. Will merge once Dependabot rebases against the current main.Worktree + branch hygiene sweep (orchestrator-direct, ~10 min). 15 locked subagent worktrees under
.claude/worktrees/agent-*(from the prior iteration's 11-subagent total) → all unlocked + removed viagit worktree remove --force. 47 local branches → deleted (32 squash-merged + 15 ancestor-merged; all safe — none had open PRs per memoryorphan-cleanup-must-exclude-open-pr-branches). 1 scratch branch (chore/scratch-2026-05-16-eod, identical to main) → deleted. Final local state: 1 worktree (repo root on main), 1 branch (main). 22 remote orphan branches remain (operator-authorized cleanup per memoryclassifier-denial-on-bulk-destructive; deferred). Briefing's shared/ auto-merge GraphQL anomaly diagnosed:gh api repos/Protocol-Wealth/pw-shared/branches/main/protectionreturns 404 "Branch not protected" — explains noisy GraphQL error on--auto; verify viastate,mergedAtnotautoMergeRequest; memory artifactshared-no-branch-protection.mdcodifies.Defense-in-depth model post-PR-Z4 (4 layers in production for outbound LLM payloads from pw-os-v2 — the production SDK call surface). Layer 1 (NEW; structural): PR-Z4 prompt-construction middleware at
apps/api/src/lib/claude-client.ts; module-level wrap; ESLint custom rule blocks bypass. Layer 2:pii.tsContract#3in-band runtime detection (cross-repo byte-identical with pw-api/pw-portal-v2). Layer 3: Independent PII egress canary (2026-05-14; byte-identical pattern set re-implemented in pw-os-v2 + pw-portal-v2). Layer 4: outbound email NPI guard at pw-api Postmark send path (PR-D1; for the email-egress class). The first-layer structural guard now exists at the production SDK site; pw-api PR-Z3 remains structural-only (pw-api still makes ZERO Anthropic calls today). pw-portal-v2 PR-Z4-portal sibling port (1 SDK egress site there) sequenced as natural follow-on; deferred to capacity-permitting.Subagent execution pattern at scale. 1 Dependabot grooming subagent this iteration (small scope). Cumulative across this day's full 4-window iteration + this same-day continuation: 12 subagents total (10 clean + 1 fix-forward + 1 Dependabot grooming). Worktree-isolation pattern via Agent tool
isolation: worktreecontinues to scale cleanly for shared/-doc work; explicitgit worktree addcontinues to be required for code-repo subagents per CLAUDE.md §6 single-agent-per-repo discipline.Decision 4 (P2.7 sub-task 3) CLEARED + Decision 1 FIRED + Decision 2 SHIPPED + Decision 3 HOLD LIFTED — all four 2026-05-17 cycle decisions are now terminal. Captured in
strategy/dispatch-next-iteration-queue.mdIteration handoff section with PR cross-references.Carry-forward for next iteration: Wealthbox inbound sync (natural follow-on to make PR
#221's skeleton useful — adds inboundGET /v1/contacts/<id>/custom_fieldsparsing path → writes to discrete columns + emitsWEALTHBOX_NPI_BACKFILL_MALFORMEDaudit events on cast failure; the skeleton's audit-action constant is already in place); PR-Z4-portal (pw-portal-v2 sibling port; 1 SDK egress site; smaller scope than PR-Z4); PWOS subagent Phase 0 PoC (substrate validated; cash flow projection deliverable; ~2-3 worker-cycles); Phase 2 worker-side polling loop (now operator-gated next); 22 shared/ remote orphan branches batch cleanup (per-action operator authorization required); pw-website pending major bumps batch cleanup (#36closed this iteration; future Vite-8-compatible re-propose).
2026-05-16 (final-anchor close — pw-website #105 + shared #161 CCO-gated landings + PR-D1 fix-forward + Decision 1 flip-to-default-on + ritual #17)
Iteration final close. Three CCO/CTO compliance-gated landings + PR-D1 fix-forward + Decision 1 polling-loop v2 flip-to-default-on fired + ritual #17 codified (Phase 6 archive must verify CI success, not just auto-merge enabled).
pw-website #105 MERGED 2026-05-16T18:45:12Z — first firm-published OS-licensing pitch surface (
/os-licensing). CCO Marketing Rule §206(4)-1 gate cleared. Subagent C from the morning batch; ~3h DRAFT → MERGED wall-clock. Code atpw-website/src/pages/os-licensing.astro(527 lines); pulls Section 1 vocabulary + Section 2 capability table fromcredential-layer-positioning.md+ Origin/PW/Verapath positioning fromcompetitive-positioning-2026-05-16.md+ OSS-wrapper architectural pattern fromPWOS.md §15.1.2. Mirrorssecurity.astrotone + disclosure discipline; SEC RIA #335298 + ADV pointer + third-party-name disclosure in footer; Calendly CTAct3f-pwd-qgm/protocol-wealth-team-discovery. Zero performance claims, zero testimonials.shared #161 MERGED 2026-05-16T18:47:00Z — ADR-PII-tagging §6 amendment with CCO + CTO double-gate cleared. Second ADR with that gate shape (PR
#143was the original; this is the first amendment). Amendment codifies that the middleware contract applies to every repo hosting an LLM SDK call surface, not just pw-api. Namespw-os-v2/apps/api/src/lib/claude-client.tsas the production SDK site requiring PR-Z4 port. Frontmatterrevision-2: 2026-05-16; gates-downstream extended withPR-Z4.pw-api PR-D1
#220— outbound email NPI guard. cli1 dispatch (third polling-loop v2 lifecycle: v2 dispatch in the controlled rollout). Operator picked (1d) audit-wrapper-layer chokepoint atemail-audit.tsinstead of orchestrator-recommended (1a) PostmarkClient.send — smarter shape; wraps the existing email-audit pattern rather than bolting onto raw client methods. cli1 built: newsrc/middleware/email-npi-guard.ts(interceptEmailSend/interceptEmailSendTemplate+EmailGuardContext/EmailGuardResulttypes +EmailNpiBlockedErrorclass) + 3 new audit-action constants (EMAIL_SEND_BLOCKED/EMAIL_SEND_AUDITED/EMAIL_WAIVER_CONSUMED) + 1 new env var (PII_WAIVER_SECRET) + 3 ESLint selectors + tests.PR-D1 CI failure + orchestrator-direct fix-forward. cli1 archived PR-D1 as
completedat 18:54:55Z whenautoMergeRequestwas non-null but CI was still in progress; Migration dry-run FAILURE landed 50 seconds earlier (18:53:41Z; not seen in cli1's poll). 3 pre-existing integration tests inemail-audit.spec.tsfailed because their fixtures didn't passpii_tagsand the new middleware correctly blocked the sends per Phase 1.5 pick 5 (default-pii.highfor untagged). Orchestrator-direct fix-forward: pass explicitpii_tagsto each failing test fixture preserving original test intent (each test still asserts the Postmark flow it was originally testing); push commit6526608to existing PR-D1 branch (no new PR); CI re-runs; auto-merge fires on green.shared PR ritual
#17codification — Phase 6 archive must verify CI success, not just auto-merge enabled (autoMergeRequestnon-null ORstate == MERGEDis insufficient when CI is still in progress). Updates worker pasteable block Step 4 with explicit poll-until-CI-terminal pattern + 10-minute poll-window cap; archives asfailed-cion any FAILURE conclusion; cross-references ritual#3(enable-step verification — companion not replacement) + ritual#9(classifier denial — one specific outcome failure mode). Cli1's 2026-05-16T18:54:55Z misfiled archive is the incident anchor. Ritual count delta: 16 → 17.Decision 1 polling-loop v2 lifecycle: v2 flip-to-default-on FIRED. 3 clean lifecycle: v2 opt-in dispatches completed: PR-Z2 (pw-api
#218MERGED 16:57:49Z) + PR-Z3 (pw-api#219MERGED 17:36:46Z) + PR-D1 (pw-api#220MERGED post-fix-forward). Substrate validated end-to-end. Future iterations default to lifecycle: v2 without per-dispatch opt-in flag. Memory artifactlifecycle-v2-rollout-3-of-3-pattern.mdcaptures the substrate validation evidence.Defense-in-depth model post-PR-D1 (4 layers in production for outbound LLM + outbound email paths). Layer 1: PR-Z3 prompt-construction middleware in pw-api (structural; ESLint-guarded; pw-api makes ZERO current Anthropic calls so this is structural guard for future). Layer 2:
pii.tsContract#3in-band runtime detection (cross-repo byte-identical). Layer 3: Independent PII egress canary in pw-os-v2 + pw-portal-v2 (egress-time backstop; deliberately re-implemented). Layer 4: PR-D1 outbound email NPI guard at pw-api Postmark send path (audit-wrapper-layer chokepoint). Plus pending PR-Z4 (pw-os-v2 middleware port; ADR §6 amendment unblocked it; queued as first work item of next iteration) closes the gap that pw-os-v2 (the production SDK call site) currently lacks the first-layer structural guard.Iteration totals (2026-05-16 full day). ~30 PRs landed across the day across 4 windows: (1) morning iteration close (
#141–#150+ pwos-core #31; Phase 0.5 spike substantively closed); (2) late-day extension (#152–#155+ pw-os-v2#335+ DRAFT#105; 4 parallel subagents); (3) later late-day extension (#157–#160+ pw-api#218+ pw-api#219+#161DRAFT +#162+#163+#164+#165; 3 parallel subagents + cli1 PR-Z2 + cli2 PR-Z3 + orchestrator PR-D1 staging); (4) final-anchor close (#166+ this PR + ritual#17+ pw-api PR-D1 fix-forward +#105+#161CCO-approved). 11 subagents total across the day (10 clean + 1 fix-forward); ritual#15worktree-locked-main fallback fired 12+ times (self-validated at scale; ritual#16+#17codified from incidents within the same day they were observed).Operator state-hygiene observations carrying forward. (a) Bulk orphan-branch cleanup must exclude open-PR branches (especially DRAFT) — codified as
orphan-cleanup-must-exclude-open-pr-branches.mdafter PR#161DRAFT was accidentally closed by cleanup, recovered viagh api -X POST refs+gh pr reopen. (b) Operator-specific Phase 1.5 picks deviated from orchestrator pre-stage once this day (PR-D1 pick 1: (1d) audit-wrapper-layer chokepoint instead of (1a) PostmarkClient.send) — workers and operators extending the orchestrator's pre-staged picks at diagnostic time is a healthy substrate pattern.Next-iteration-briefing.md refresh anchors: PR-Z4 (pw-os-v2 middleware port) as first work item against validated lifecycle: v2 default-on substrate; Wealthbox NPI promotion second priority (cli2; scoping doc at
strategy/wealthbox-npi-fields-promotion-plan.mdper PR #163); lifecycle: v2 default-on flip codification as orchestrator-direct mechanical work (dispatch/protocol/lifecycle.md+worker-launch-ritual.md+orchestrator-daily-start-prompt.mdcount + flag references).
2026-05-16 (later late-day — Stream Z PR-Z2 + PR-Z3 land in pw-api + Stream D PR-D1 staged + ADR §6 DRAFT + ritual #16 + Wealthbox NPI scoping)
Second operator-extension window in the same day. cli1 (PR-Z2) + cli2 (PR-Z3) ran sequentially; both landed clean in pw-api with lifecycle: v2 substrate. PR-D1 (Stream D outbound email NPI guard) drafted + staged for cli1 — third dispatch of the Decision 1 controlled rollout. 3 subagents (A/B/C) spawned in parallel during the wait/drafting window. Decision 1 flip-to-default-on threshold met: 3 clean lifecycle: v2 dispatches (PR-Z2 + PR-Z3 + PR-D1 staging completing); future iterations default to lifecycle: v2 without per-dispatch opt-in flag.
pw-api
#218— PR-Z2 pii_tags column on client-data ingestion tables (94125c4, MERGED 2026-05-16T16:57:49Z). cli1 dispatch; first polling-loop v2 lifecycle: v2 opt-in dispatch. JSONB per-field shape (pii_tags JSONB NOT NULL DEFAULT '{}'); 11 tables migrated (clients, households, external_identities, quiltt_profiles, quiltt_connections, accounts, quiltt_transactions, altruist_households, altruist_accounts, client_assessments, client_wallets); 13 INSERT call sites updated; 68 backfill JSONB keys; canonical typed map atsrc/lib/pii-tags.ts. Test baseline exact (0 delta). 6 CI checks all SUCCESS. Phase 1.5 picks: JSONB shape + CTO-only gate + explicit backfill topii.highper-text-column + Wealthbox custom NPI fields tagged asclients.metadatapii.highcatch-all (future PR promotes to discrete columns per the scoping doc landed below).pw-api
#219— PR-Z3 prompt-construction exclusion middleware (MERGED 2026-05-16T17:36:46Z). cli2 dispatch; second lifecycle: v2 opt-in dispatch. Newsrc/middleware/pii-prompt-construction.ts(~350 lines) exportingexcludeHighPii/excludeHighPiiByTable/verifyWaiver/memoryJtiStore/redisJtiStore; 5 ADR §6 canonical event names added tosrc/lib/audit-actions.ts(PII_FIELD_EXCLUDED/PII_WAIVER_GRANTED/PII_WAIVER_CONSUMED/PII_WAIVER_REJECTED/PII_MEDIUM_INCLUDED);PII_WAIVER_SECRETenv var declared (operator provisions GCP Secret Manager out-of-band); ESLintno-restricted-syntaxextended with 3 selectors blocking direct@anthropic-ai/sdk/openaiimports outside the middleware module; 18 new tests; cli2 invented the ESLint sentinel validation pattern at Phase 4 (later codified as ritual#16below). cli2 added a 5th Phase 1.5 pick (untagged-payload posture) beyond the orchestrator's pre-stage — workers extending the pick set at diagnostic time is a healthy substrate pattern. pw-api has ZERO Anthropic SDK call sites today (consistent with CURRENT-STATE.md AI surface anchor); middleware ships as structural guard for future LLM client additions in pw-api.shared #157 — cli1 PR-Z2 task completed; archived (1ed8add, MERGED). 83-line completion archive at
dispatch/cli1/done/2026-05-16-165906-completed.mdcapturing 4 Phase 1.5 picks + 11-table substitution count + Phase 4 verification + lifecycle: v2 substrate observations. Archived via ritual#15worktree-locked-main fallback (cli1'sgit checkout mainfailed; routed via branch + PR + auto-merge).shared PRs
#158+#159— PR-Z3 activation to cli2 + cleanup of stale cli1 pending.md (79d8124 + 0fcb974, both MERGED). Operatorgit mv'ddispatch/shared/pr-z3-staged-dispatch.mdtodispatch/cli2/pending.md+ paste-launched cli2; followed by orchestrator-direct cleanup of cli1's stale pending.md (a carry-over from the worktree-isolated archive pattern that couldn't span worktrees for atomic git mv).shared #160 — cli2 PR-Z3 task completed; archived (632c756, MERGED). Completion archive at
dispatch/cli2/done/2026-05-16-XXXXXX-completed.md; ritual#15fallback fired again for the archive PR.shared #161 (DRAFT) — ADR-PII-tagging §6 amendment naming pw-os-v2 claude-client as parallel implementation site (subagent A; isDraft=true; awaiting CCO). Codifies that the middleware contract applies to every repo hosting an LLM SDK call surface, not just pw-api. PR-Z3 shipped to pw-api as structural guard; pw-os-v2's
apps/api/src/lib/claude-client.tsis the actual production SDK site (per the 2026-05-16 subagent D scope correction in pw-os-v2 #335). Frontmatterrevision-2: 2026-05-16; gates-downstream extended withPR-Z4 (pw-os-v2 middleware port). Explicit CCO Marketing Rule + Reg S-P §248.30(b) safeguards-rule gate in PR body.shared #162 — ritual
#16ESLint sentinel validation pattern (94d80d6, MERGED). Codifies cli2's PR-Z3 Phase 4 invention: when adding restrictive ESLint rules, add a sentinel file with the forbidden pattern + a paired sentinel in an allowlist path; run lint to verify both fire/don't-fire as expected; delete sentinels; re-lint clean; THEN commit. Catches silent-selector-typo and allowlist-scope-too-broad failure modes. Cross-references ritual#3(auto-merge enable + verify) as companion verification-discipline pattern. Ritual count delta: 15 → 16; updated inworker-launch-ritual.md+next-iteration-briefing.md+orchestrator-daily-start-prompt.md+dispatch-next-iteration-queue.md.shared #163 — Wealthbox NPI fields promotion-from-metadata plan (b9023a1, MERGED). 197-line scoping doc at
strategy/wealthbox-npi-fields-promotion-plan.md. Pre-stages tomorrow's cli2 dispatch on promoting the 6 ADR §4 Wealthbox custom NPI fields fromclients.metadataJSONB catch-all to discrete typed columns. 4 fields promoted to discrete columns (tax_filing_status / dependents_count / irs_backup_withholding / control_person / finra_affiliation); Telegram handle stays in metadata. Copy-paste-ready migration SQL + Wealthbox API field-mapping + cast-with-fallback backfill (10% malformed-cast threshold sentinel) + Phase 1.5 picks pre-staged for cli2.shared #164 — PR-D1 dispatch staging to cli1 (5df8fce, MERGED). Stream D PR-D1 implementation per credential-layer-positioning.md §3 Gap 3 (outbound email NPI guard / PII-aware retrieval). Consumes PR-Z3 middleware; interceptor candidates at
pw-api/src/lib/email.tsPostmarkClient class; Phase 1.5 SURFACE pre-stages 5 picks (interceptor location / fail-closed posture / audit event names matching/^[a-z]+(\.[a-z_]+){1,2}$/regex / template merge-field handling / untagged-payload posture matching PR-Z3). Auto-merge fired clean.Decision 1 lifecycle: v2 rollout 3-of-3 readiness signal. PR-Z2 (#218) + PR-Z3 (#219) + PR-D1 (staged) completes the controlled 3-dispatch rollout. PR-D1 must complete to fully trigger the flip; the staging itself is the third dispatch's birth event. Per saved memory artifact
lifecycle-v2-rollout-3-of-3-pattern.md: substrate validated (atomic state.md writes; clean Phase 1.5 SURFACE behavior; ritual#8STOP discipline honored across all 3); future iterations default to lifecycle: v2 without per-dispatch opt-in.Defense-in-depth model post-PR-Z3 (4 layers). Layer 1: PR-Z3 prompt-construction middleware in pw-api (structural; ESLint-guarded). Layer 2:
src/lib/pii.tsContract#3in-band runtime detection (cross-repo byte-identical). Layer 3: Independent PII egress canary in pw-os-v2 + pw-portal-v2 (egress-time backstop; deliberately re-implemented). Layer 4 (in flight via PR-D1): outbound email NPI guard at pw-api Postmark send path. Plus pending PR-Z4 (pw-os-v2 middleware port) closes the gap that pw-os-v2 (the production SDK site) currently lacks the first-layer structural guard.Ritual
#15worktree-locked-main fallback 5+ reproductions in this batch. Every shared/ archive PR (subagent A, subagent B, subagent C, PR-D1 staging, cli1 archive, cli2 archive) hit the worktree-locked-main case duringgh pr merge --delete-branch; retry without--delete-branchsucceeded each time; orphan remote branches cleaned viagh api -X DELETEpost-merge. The pattern has now self-validated 8+ times since codification at PR#153(2026-05-16 morning). Polling-loop v2 Phase 2 per-worker git worktrees eliminates this structurally; until then, it's the multi-subagent-iteration environmental constant.Subagent worktree-isolation at scale (second batch this iteration). 3 subagents (A: ADR §6 amendment DRAFT; B: ritual
#16codification; C: Wealthbox NPI scoping doc) spawned in parallel via Agent tool withisolation: worktreeon shared/. All 3 landed cleanly within ~5 minutes wall-clock from spawn; A held in DRAFT per directive, B + C auto-merged. Combined with first-batch 4 subagents earlier in the day (total 7 subagents this iteration; 6 clean + 1 DRAFT awaiting CCO). The 6-subagent 2026-05-16 batch + 7-subagent 2026-05-16-later batch validate worktree-isolation as the canonical multi-subagent shared/-work pattern at full production scale.
2026-05-16 (late-day — iteration extended with 4 parallel subagents + PR-Z2 dispatch + ritual #15 codification + hook tightening + pw-os-v2 multi-user identity fix)
Operator extended the 2026-05-16 iteration past the briefing-refresh anchor (#151) for a ~1h autonomous-orchestrator push-out window. 5 PRs landed within ~25 min wall-clock (#152 housekeeping at 15:25Z → pw-os-v2 #335 at 15:45Z); 1 DRAFT opened (pw-website #105 awaiting CCO).
#152(chore/iteration-close-housekeeping) — archivedispatch/cli2/post-compact-observation-2026-05-16T14-25-00Z.md+resume-observation-2026-05-15-195700.mdto per-spikearchive/2026-05-{15,16}-{tier1,tier2}-snapshots/directories per spike-findings §8 citation conventions.#153(subagent A; ritual#15codification) —dispatch/shared/worker-launch-ritual.mdadds hardening item#15: whenmainis locked by another worktree (e.g., agent-tool worktree at.claude/worktrees/agent-*), route archive commits via branch + PR + auto-merge instead of direct push. Aligns with CLAUDE.md §1.4 PR-not-direct-push preference; worktree-locked-main case promotes the preference to a hard requirement. Worker pasteable block Step 3 updated with inline fallback note. Ironic self-validation: the codification PR's owngh pr merge --delete-branchhit the exact failure mode being codified; retry without--delete-branchsucceeded. Reproduced 3× in 10 min across PR#153+#154+#155.#154(subagent B;security_reminder_hookpath-allowlist tightening) — new doc atstrategy/security-reminder-hook-tightening.md. Investigation found the hook is plugin-managed at~/.claude/plugins/marketplaces/claude-plugins-official/plugins/security-guidance/hooks/; direct git-tracked tightening not feasible (plugin-source read-only). PR-able surface is the documentation entry capturing recommended user-side~/.claude/settings.jsonwrapper hook + 3-iteration false-positive history (CHANGELOG#11/§102/§113; cli2 archive atdone/2026-05-16-104419-completed.md:284; tier2-snapshot atarchive/2026-05-16-tier2-compaction-snapshots/d2-post-compact-observation.md:63-66). Tightening shape scoped specifically to concurrency-only field edits on.github/workflows/*.yml(concurrency.group uses GitHub-controlled context vars; not therun:injection surface).#155(orchestrator; queue-file refresh + cli1 PR-Z2 dispatch) —strategy/dispatch-next-iteration-queue.mdrefreshed from 2026-05-15 anchor to 2026-05-16 close (Queue A: Tier 1 attempt-4 DE-QUEUED + Tier 2 COMPLETED + Phase 1 lifecycle.md authored + adoption work active; Queue B: 2026-05-17 dispatches priority-ordered; Decisions 1-4 codified).dispatch/cli1/pending.mdwritten with first polling-loop v2 lifecycle: v2 opt-in dispatch (Decision 1 — opt-in flag on first ~3 dispatches; flip default-on after 3 clean). PR-Z2 dispatch:pii_tagcolumn on client-data ingestion tables per ADR-PII-tagging.md (PR #143); Phase 1.5 SURFACE pre-staged with 4 picks (table scope / column shape / backfill posture / CTO+CCO concurrent gate question). PR-Z3 (cli2) staged atdispatch/shared/pr-z3-staged-dispatch.mdper Decision 2 sequencing.pw-os-v2
#335(subagent D; PWOS multi-user identity fix) — fixes a hardcoded two-name team-reference string in PWOS chat agent's system prompt. Scope correction: the hardcoded string lived atpw-os-v2/apps/api/src/lib/claude-client.ts:163, NOT in pw-api as initially scoped — pw-os-v2's apps/api is the chat-mediating server; pw-api makes ZERO Anthropic calls (CURRENT-STATE.md anchor). Fix: newapps/api/src/lib/partners.tswithgetTeamReference(userEmail)helper reading from canonical PARTNERS roster; excludes current user; convertsDEFAULT_SYSTEM_PROMPTto template with{{TEAM_REFERENCE}}token; both/messagescreate + regenerate routes templated per-request. Test fixture covers 3 principal-to-team-reference mappings (each principal's prompt names the other two partners) + 4 fallback cases (unknown email / null / undefined / empty string → "your partners"). 1113 tests pass; auto-merge fired immediately on all 6 CI checks green (TypeScript / Tests / ESLint / Build / Security / Compliance lint).pw-website #105 (subagent C; OS-licensing pitch surface — DRAFT) — 527-line
src/pages/os-licensing.astromirroringsrc/pages/security.astrotone + disclosure discipline. Pulls Section 1 vocabulary + Section 2 capability table fromshared/strategy/credential-layer-positioning.md; Origin/PW/Verapath positioning fromshared/strategy/competitive-positioning-2026-05-16.md; OSS-wrapper architectural pattern fromshared/architecture/PWOS.md §15.1.2; Calendly CTAct3f-pwd-qgm/protocol-wealth-team-discovery.noindexwhile review is pending; in-page DRAFT banner; SEC RIA #335298 + ADV pointer + third-party-name disclosure + capability-status-honesty note in footer. Zero performance claims, zero testimonials, zero superlatives. CCO Marketing Rule §206(4)-1 review gate explicit in PR body; auto-merge NOT enabled per operator directive. Missing-CCO-memo-v2-supplement noted in PR body.Subagent worktree-isolation at scale validated again — 4 subagents spawned in parallel via Agent tool with isolation=worktree (shared/) for A + B; explicit
git worktree add /tmp/pw-os-v2-subagent-d-multi-userfor D in pw-os-v2; C operated in pw-website without isolation needed. All 4 landed cleanly; ~25 min wall-clock from spawn to last-merge. Codified as canonical pattern in memory artifactsubagent-worktree-isolation-canonical.md. 2026-05-16 ran 6 subagents (5 clean, 1 chat-surface review for PR #141); 2026-05-17 open ran 4 (3 clean + 1 DRAFT per directive).3 orphan remote branches cleaned post-merge —
gh api -X DELETE refs/heads/chore/{ritual-hardening-15-worktree-locked-main-fallback,security-reminder-hook-path-allowlist-investigation,iter-2026-05-17-open}— all three PRs hit the ritual#15worktree-locked-main case duringgh pr merge --delete-branch; remote refs persisted post-squash-merge. Local prune also removed 5 stale orphan refs from prior iterations.Operator-approved Decisions 1-4 codified in queue-file — Decision 1 (Phase 1 lifecycle: v2 opt-in on first ~3 dispatches); Decision 2 (Stream Z Z2 first to cli1, Z3 auto-dispatch to cli2 after Z2 lands); Decision 3 (PWOS Phase 0 PoC hold one cycle until v2 substrate validates with 3 clean dispatches); Decision 4 (P2.7 sub-task 3 subagent-DRAFT only; CCO Marketing Rule §206(4)-1 gate on publish). PR-Z2 dispatch is rollout dispatch
#1; subagents A-D occupied the iteration's parallel-execution capacity.
2026-05-16 (orchestrator iteration close — 10 shared/ PRs + pwos-core #31; Phase 0.5 spike substantively closed; first CCO ADR gate cleared; PWOS positioning sharpened)
Three foundational themes anchor this iteration. (1) Phase 0.5 polling-loop v2 compaction spike substantively closed — Tier 2 actual-compaction (pwos-core target via PR
#147/ pwos-core #31) produced a clean recovery-clean outcome with high-fidelity d2 recall + d4 self-aware compaction narration + protocol-adherent disk re-read discipline; §9 recommendation flipped provisional → empirically confirmed (option b checkpoint-based-resume). Phase 1 lifecycle.md (#142) was authored against option (b); design holds. Tier 1 attempt-4 obviated. (2) First CCO ADR gate cleared — Stream Z PR-Z1 PII-tagging ADR (#143) shipped with explicit CTO+CCO sign-off, validating the CCO ADR-review flow as a production pattern; unblocks Stream Z PR-Z2 + PR-Z3 (both pw-api worker dispatches). (3) Strategic positioning sharpened — OSS-wrapper pattern operationalized in PWOS.md §15.1.2 (#141); ROADMAP gained P2.12 PWOS multi-agent orchestration (#145); P2.6 commercial-precedent register expanded to 5 names (Supabase, Tailscale, dbt Labs, Anthropic added alongside MongoDB/Red Hat); Origin/Verapath/PW competitive landscape analyzed atstrategy/competitive-positioning-2026-05-16.md(#146); PW positioning sweet spot is the B2A2C OS-licensing tier between Origin (autonomous-consumer extreme) and Verapath (on-prem enterprise extreme).pw-shared #141 — PWOS.md §15.1.2 OSS-wrapper fold-in (51916e5, merged 14:48:56Z). Structural firm-state edit; operator approved via chat-surface 5-line summary (no PR-file open); first instance of the chat-surface-review pattern codified as carryforward learning. Sub-section frames OS-licensing thesis around the Supabase/Tailscale/dbt-Labs commercial-precedent set.
pw-shared #142 — Polling-loop v2 Phase 1 worker lifecycle protocol (e23a0a5, merged 14:00:20Z). Authoring at
shared/dispatch/protocol/lifecycle.md(456 lines); checkpoint-based-resume substrate per Phase 0.5 §9 provisional option (b). Phase 1 design holds post-Tier-2 empirical confirmation later in the iteration.pw-shared #143 — Stream Z PR-Z1 PII-tagging ADR (0a3e1c6, merged 14:52:46Z). 192 lines at
architecture/decisions/ADR-PII-tagging.md; pii.{high,medium,low} taxonomy + exclusion-middleware shape; CTO + CCO double-gate cleared (first ADR with explicit CCO gate landing). Unblocks PR-Z2 (pii_tag column on client-data ingestion tables; pw-api worker dispatch) + PR-Z3 (prompt-construction exclusion middleware; pw-api worker dispatch).pw-shared #144 — PWOS multi-agent architecture sketch (b94e410, merged 14:02:25Z). 418 lines at
architecture/PWOS-multi-agent-sketch.md; Conductor + deliverable-subagents shape (retirement analysis, financial plan, cash flow projection, proposal generation) with scoped Wealthbox/Quiltt/Mercury read access + Word/PDF/Google Docs output via existing pandoc + xelatex toolchain. Detailed-enough to dispatch PWOS Phase 0 PoC against; sketch supersedes priorpwos.app/workstreamsinformal capture.pw-shared #145 — ROADMAP P2.12 PWOS multi-agent orchestration + P2.6 OSS-wrapper precedents (89980ba, merged 14:04:50Z). P2.12 added as the productized-multi-agent vector; P2.6 commercial-precedent register expanded from 2 names (MongoDB, Red Hat) to 5 (added Supabase, Tailscale, dbt Labs, Anthropic) — each ships an open-source core + builds commercial value via hosted infra / paid features / proprietary extensions, mirroring the PWOS licensing thesis.
pw-shared #146 — Competitive positioning analysis (Origin + Verapath + PW) (b8de596, merged 14:07:32Z). 385 lines at
strategy/competitive-positioning-2026-05-16.md. Anchors PW between Origin Financial (autonomous-non-discretionary consumer extreme — speed + scale but no fiduciary HITL) and Verapath (on-prem enterprise extreme — sovereignty but high friction); PW occupies the B2A2C OS-licensing sweet spot via CCO HITL Tier 2 review + multi-RIA productization vector. Canonical comparison anchor for OS-licensing pitch + investor briefings.pw-shared #147 — cli2 Tier 2 dispatch + BlockSkunk Phase 0/1 deliverable + COMPLIANCE-CHANGELOG entry (89f224c, merged 14:09:09Z). 4 files / 1 PR bundling pattern: (a)
dispatch/cli2/pending.mdfor Tier 2 actual-compaction spike vehicle dispatch (target: pwos-core concurrency-block on 2 workflows); (b)compliance/audits/blockskunk-phase-0-1-deliverable.pdflanded (unblocks Reg S-P Compliance Record §4 finalization per ROADMAP P0.7); (c) COMPLIANCE-CHANGELOG entry per Rule 204-2 audit-trail discipline. Operator-touchpoint cost: 1 PR, not 3 — codifies bundling pattern for routine doc maintenance.pwos-core #31 — Tier 2 compaction spike vehicle (cf6df62, merged 14:41:22Z).
ci: add concurrency block to 2 workflows— addedconcurrency: cancel-in-progressto.github/workflows/license-compliance.yml+.github/workflows/spdx-headers.yml. Meaningful infra hygiene (CI workflow-run dedup) independent of spike outcome. cli2 dispatched against this target, completed Phase 2, narratedawaiting: compact, operator fired/compact, cli2 resumed cleanly post-compact, finished Phase 5+6, PR merged. Tier 2 outcome: recovery-clean empirical signal — d2 recall preserved pre-compact state across compaction boundary; d4 worker self-narrated the compaction event in observation file; protocol-adherent disk re-read (no working from memory).pw-shared #148 — P2.7 reframe + new P3.11 + PWOS-multi-agent-sketch §4.5 Verapath fold-in (502efce, merged 14:16:04Z). P2.7 reframed as complementary-not-competitive vs Verapath (different deployment tier); new P3.11 codifies CCO HITL Tier 2 review as the PW differentiator vs Origin autonomous-non-discretionary posture; §4.5 added to PWOS-multi-agent-sketch.md with explicit Verapath comparison context for Phase 0 PoC design.
pw-shared #149 — cli2 Tier 2 archive (2ea7191, merged 14:44:47Z).
dispatch/cli2/done/2026-05-16-104419-completed.md(full Tier 2 completion report) + d2post-compact-observation-2026-05-16T14-25-00Z.md(pre-disk-read recall capture; high-fidelity per spike protocol).pw-shared #150 — Spike findings §8.2 empirical + §9 confirmed (19d839f, merged 14:53:25Z). Tier 2 results synthesized: clean recovery within full test scope (not narrowed-scope like Tier 1 attempt-3). §9 recommendation: option (b) checkpoint-based-resume empirically confirmed for the original spike's primary question (mid-edit recovery via on-disk artifacts post-compaction event). Phase 1 lifecycle.md (#142) holds; Tier 1 attempt-4 DE-QUEUED.
State-narration
awaiting: compactenum extension landed as substrate for Tier 2 — first iteration extending the polling-loop v2 Phase 2 precursor (originally engineered forawaiting: killin Tier 1 attempt-3) to the compaction lifecycle event. Substrate worked perfectly first-try; no architectural reshuffle required. Banks toward Phase 2 generalization to every phase boundary + heartbeat-staleness detection.Subagent worktree-isolation pattern validated at scale. 6 subagents spawned across this iteration in isolated worktrees prevented
.git/indexrace entirely; 5 landed clean PRs autonomously, 1 surfaced for operator review (PR#141chat-surface review pattern). Codifies worktree-isolation as the canonical multi-subagent pattern for shared/ doc work — distinct from cli{1,2} worker dispatches to code repos.security_reminder_hookPreToolUse hook false-positive — third consecutive iteration fired on workflow Edit/Write (this iteration's pwos-core #31 mechanical workflow concurrency-block insertion). Promoted to hard-tracked review item; carry-forward to next-iteration operator-direct triage.CTO/CISO + CCO sole approval. Stream Z PR-Z1 ADR carries explicit CTO + CCO sign-off (first instance). All other PRs are CTO/CISO-only routine doc maintenance per CLAUDE.md §1.4. No live compliance-content change requiring CCO review beyond the ADR gate.
2026-05-15 (orchestrator morning iteration close — ~12 PRs landed, duplicate-work detection codified, P2.7 sub-task 1 + Phase 0.5 spike plan + pw-website CI hardening)
Tight 2-hour focused iteration running ~10:00Z–12:15Z built on the prior ~03:30Z close. 12 PRs landed: shared/
#114,#115,#116,#117,#118,#119,#120+ 2 archive commits; pw-website #100 +#101(operator-prompted CI work) +#95(astro 6.1.10 dependabot auto-merge).Credential-layer-positioning canonicalization (ROADMAP P2.7 sub-task 1) DONE via orchestrator-direct PR
#118. Doc landed atshared/strategy/credential-layer-positioning.md(402 lines, byte-identical to operator's v0.3 desktop draft). 4 incoming refs updated (ROADMAP P2.7 + dispatch-next-iteration-queue Queue B + next-iteration-briefing 2 lines). Pre-canonicalization audit found zeroPW/pw-os/shared/body refs (frontmatter already canonical-shape). Sub-tasks 2 (PWOS.md §15.1 fold-in) + 3 (pw-website pitch surface) + 4 (Stream Z/A/B/C roadmap items) sequenced.Ritual hardening
#10(Phase 0 already-landed pre-flight) codified via PR#116— bundled with orchestrator-prompt step 0 + overnight-diagnostics §5 enumeration of recently-merged PRs. Driven by TWO duplicate-work findings this iteration: pw-website-weekend-hygiene + nexus-core-weekend-hygiene worktrees both showed unpushed local commits but the work had already landed via separate PRs (#98+#18, both merged 2-4h before the overnight diagnostic ran). Two-layer defense (orchestrator-side step 0 + worker-side ritual #10) catches duplicates at either dispatch-authoring time OR worker Phase 0 boundary.pw-website CI Node 20→22 bump via PR
#100(cli2 dispatch) unblocks astro@6 dependabot bumps + clears Node 20 EOL pressure. Sibling-repo alignment (pwos-core / pw-os-v2 / pw-api on 22.x+).Auto-merge workflow hardening (pw-website #101) — orchestrator-direct after Step 2.2 investigation root-caused the devalue PR
#9730-min hang. Workflow now: (a) names which checks are still running (was:"1 checks still running"without identification); (b) 10-min phantom-check escape hatch surfaces named culprit if any check hasstarted_at>10 min withstatus != "completed". Future hangs surface their cause + escape within 10 min instead of 30. No change to merge semantics.Polling-loop v2 Phase 0.5 spike plan drafted via PR
#119(no auto-merge; operator-reviewed + merged 2026-05-15T12:04:47Z). Living document atshared/strategy/dispatch-compaction-spike-findings.md. Resolves: which compaction-recovery posture Phase 1's state machine implements — (a)FAILED_COMPACTIONauto-archive + re-dispatch, or (b) checkpoint-based resume via in-progress.md frontmatter. Experimental tiers: 1 simulated (kill+restart fresh worker; cheap proxy), 2 actual compaction attempt (deliberately context-heavy task; up to 3 attempts), 3 API instrumentation (fallback). Empirical phase queued for next iteration.Two cli dispatches landed cleanly: cli2 PR
#115(pw-website Node bump dispatch) → cli2 PR#100(target work) → archivef3768a2. cli1 PR#114(pw-website-weekend-hygiene push dispatch) → DUPLICATE-WORK SURFACE (target had merged as PR#98~3.5h before overnight diagnostic ran; cli1 pushed + opened PR#99, then operator-relayed finding caught it;#99closed; cli1 archivedsurfaced-operator-decision-onlyatdb8b457). Lessons fed into ritual#10codification.dispatch-next-iteration-queue refresh via PR
#117— was stale by 2 iterations; now anchors current state with cascade-close lookback + this iteration's in-flight + Queue A (polling-loop v2 unchanged) + new Queue B (Tier-1 strategic candidates per briefing Decision 1) + new Queue D (major-version dependabot bumps awaiting operator review).Memory artifact added:
orchestrator-direct-threshold.mdcaptures operator guidance — trivial git/gh ops + simple text updates run orchestrator-direct; non-trivial code-affecting work dispatches to a worker. Calibration: if dispatch overhead ≈ work itself, run direct.Operator-direct cleanups completed: orphan remote ref
pw-website/docs/2026-05-15-weekend-hygiene-shared-refsdeleted viagh api -X DELETE;cleanup-worktrees.sh --apply --forcecleared bothpw-website-weekend-hygiene+nexus-core-weekend-hygieneworktrees (both confirmed duplicate-of-landed; no work-loss).Dependabot rebase queue (pw-website 8 → 6 open):
@dependabot rebasetriggered on all 8 PRs after#101's new diagnostic workflow shipped.#93(minor-patch group) +#95(astro 6.1.10) +#55(astro 5→6 duplicate) cleared;#103(new minor-patch group post-rebase) appeared. Remaining: 3 non-major (#71postcss /#92fast-uri /#97devalue) processing through new workflow; 2 major (#36vitejs /#53typescript) sit for operator review. Note:#95was a major bump that auto-merged unexpectedly — worth verifying classification + Tests-on-Node-22 next inspection.cli1 follow-up dispatch queued via PR
#120— PWOS.md §15.1 credential-layer fold-in (P2.7 sub-task 2). Worker dispatched with 3 fold-in shape options (A: single bullet, B: two bullets, C: new §15.1.1 sub-section); mandatory Phase 1.5 SURFACE before applying; PR WITHOUT auto-merge per CLAUDE.md §1.4 (structural change to canonical firm-state doc). Operator launched cli1 ~12:10Z (mid-iteration); cli1 will SURFACE then STOP awaiting operator pick on shape since operator stepped away.
2026-05-15 (orchestrator iteration close — 25 PRs landed, doc-cleanup arc complete, multi-cycle protocol validation)
25 PRs landed in this orchestrator iteration spanning shared/, pw-api, pw-os-v2, pw-portal-v2, pw-website, pw-infrastructure, pwos-core, nexus-core. The iteration opened with the cli1 + cli2 Phase 3 ALREADY-BROKEN cleanup (synthesized in the entry below) and progressed through three structural protocol improvements + Queue B/C/D doc-cleanup arc completion + classifier-layer permissions findings.
PR-not-direct-push convention codified for shared/ doc maintenance (shared #94): replaced the worker-launch-ritual's direct-push-to-main pattern with PR-with-auto-merge for routine doc writes. Codified
shared/CLAUDE.md§1.4 + worker-launch-ritual Step 3 + cross-reference inmulti-agent-coordination.mditem 7. Codification driven by three live forcing moments — PR#88(read-from-disk launch pattern PR'd not direct-pushed despite ritual saying direct push), classifier denials of orchestrator direct-push attempts, and the 83f346b incident where cli2'sgit add -Aswept 15 lines ofCURRENT-STATE.md+ 29 lines ofdispatch-next-iteration-queue.mdorchestrator WIP into a worker archive commit.Scoped
git addritual hardening (shared #94, same PR): Step 3 now usesgit add dispatch/cliN/done/instead ofgit add -A. Multi-cycle validated across every subsequent archive commit (cli1 + cli2, both repos): each archive stats as 1 file rename + N insertions, zero swept WIP.Phase 1.5 STOP override-strong wording (shared #96 dispatch + carried forward): explicit per-dispatch language that dispatch-level Phase 1.5 SURFACE-TO-OPERATOR stops OVERRIDE the launch-prompt's global "work without stopping for clarifying questions" directive. Recursive validation arc: cli2 PR
#332deviation → PR#96codification → PR#333honored stop → PR#4regression → PR#19named-prior-bypass framing held. Per-dispatch wording is the iteration-stopgap; ritual-level codification queued for next-iteration hardening pass.Auto-merge classifier permissions layering finding:
~/.claude/settings.jsonpermissions.allowforBash(gh pr merge *)was added to bypass classifier denials. Resolved 4-of-5 subsequent scenarios but cli1 PR#191surfaced the remaining failure mode — classifier reasons over message-level intent ("'proceed with B' approved the rewrite, not self-merging"), not just command pattern.permissions.allowis at the standard prompt layer; classifier is a separate intent-evaluation layer (autoModesettings section). True structural fix is polling-loop v2's "worker proposes; orchestrator handles via API with explicit auth context" pattern. Worker dispatches now include "expect classifier may deny first attempt; if denied, surface immediately and await operator unblock" guidance.Queue B (operator-decision content fixes) cleared 4-of-4 + bonus row: pw-os-v2
#332(PW-SECURITY-POSTURE glob + cdf.md aspirational ref) + pw-infrastructure#188(open-source-inventory + covered-ui-checklist legacy refs) + bonus pw-os-v2#333(Shared docs section coherent rewrite, surfaced by cli2 in #332). Plus PW-ARCHITECTURE-DEEP-DIVE archive divergence resolved: pw-shared #91 (dedupe to canonicalarchive/state/) + pw-api#216(realign refs); pw-os-v2 already aligned.Queue C (
canonical-references.mdrewrite) landed via option B pointer-replacement (pw-infrastructure#189; cli1 audit revealed 25 surfaced refs were actually 37 distinct doc citations: 52% fabricated / 36% wrong-pathed / 12% correct in pre-rewrite content). Followed by the 11-incoming-ref reconciliation (pw-infrastructure #190) + canonical-ops-section rewrite in pw-infrastructure CLAUDE.md (pw-infrastructure #191). Three-PR arc: replace canonical doc → reconcile incoming refs → trim adjacent stale section.Queue D (weekend hygiene) — four bundles delivered: pw-website #98 (14 uppercase
SHARED/refs + 7 legacy absolute-path refs in GOVERNANCE-BASELINE.md retargeted; that doc relabeled as historical baseline); pwos-core #20 (README Fly.io qualified with PW Cloud Run note + dependabot.yml provisioned + 7 SPDX backfills); nexus-core #4 (pyproject.toml FMP comment fix + dependabot.yml provisioned + 20__init__.pySPDX backfills); nexus-core #19 (CLAUDE.md authored from scratch using pwos-core/CLAUDE.md as sibling pattern).Boomer / Kelp Financial deal structure dropped (per operator decision): ROADMAP P2.7 (revenue tracking for monthly Boomer disbursements) + P3.4 (Kelp Financial acquisition deal structure) + CURRENT-STATE Decision queue entry + CCO Friday-meeting prep mention all removed. Frees CCO capacity for Reg S-P / Reg XP June 3 deadline + DocuSign signing queue. Vault Summit (June 5), Open Cover insurance, Farmhouse attorney follow-up retained on CIO queue.
PWOS subagent productization concept surfaced for post-doc-cleanup horizon: extend the orchestrator multi-agent pattern into PWOS itself — main PWOS chat agent spawns deliverable subagents (retirement analysis, financial plan, cash flow projection, proposal generation) with scoped Wealthbox/Quiltt/Mercury/etc. read access + Word/PDF/Google Docs output via existing pandoc + xelatex toolchain. Phase-0 PoC → Phase-1 first production deliverable → Phase-2 productize sequencing proposed. Highest-strategic-leverage product investment available; sequenced behind doc-cleanup completion + polling-loop v2 Phase 0.5 spike + pw-onchain absorption Slices 4+.
CTO/CISO sole approval. Doc + dispatch infrastructure + global Claude Code classifier-rule settings; no compliance content modified; no firm-state behavior change; no production code touched outside the public-repo hygiene + per-repo CLAUDE.md retargets.
2026-05-14 (Phase 3 ALREADY-BROKEN cleanup landed — 4 PRs auto-merged)
- Four cross-repo cleanup PRs landed in a 3-minute window following the cascade iteration close; cli1 + cli2 dispatched at 9848b3b worked sequentially across distinct worktrees and all four auto-merged on green CI: pw-os-v2
#331(9e2e570, 20:50:13Z) → pw-api#215(0dc9663, 20:51:00Z) → pw-infrastructure#187(b76b251, 20:51:55Z) → pw-portal-v2#59(f0390d7, 20:53:00Z). All Phase 3 ALREADY-BROKEN path substitutions deferred from the cascade PRs (#214/#58/#330/ #186) now folded in. Total: 41 substitutions across 36 files — pw-api 23 files / 33 lines (drift +3 vs ~20 expected; within ±5 STOP gate; driven by one line-wrappedPW-ARCHITECTURE-DEEP-DIVEref inmigrations/0017that the\.md-anchored Phase 1 diagnostic missed + twoshared/api/*glob refs in strategies modules); pw-os-v2 8 substitutions across 7 files (drift 0); pw-infrastructure 4 substitutions across 4 files (drift 0); pw-portal-v2 6 substitutions across 3 files (drift 0). - Auto-merge discipline validated end-to-end on its first multi-repo test post-ritual-hardening. cli2 closed the gap from
#330(autoMergeRequest non-null on both pw-os-v2#331+ pw-infrastructure#187within 60s of PR create per setpoint #1); cli1 likewise on pw-api#215+ pw-portal-v2#59. Ritual hardening addition#3(auto-merge enable + verify) survives contact with reality on its first real multi-repo run. - Phase 4b runtime-concat sweep yielded zero hits across all four repos — second confidence-building negative validation of the sweep pattern added after pw-website #96 surfaced the regex-blind spot at
scripts/compliance-lint.mjs:242. - Operator-decision item surfaced (both workers, independently):
shared/archive/state/PW-ARCHITECTURE-DEEP-DIVE-2026-04-24.md(48382 bytes, md5 6be674dda968de9841ff14877387d2cf) andshared/archive/status/PW-ARCHITECTURE-DEEP-DIVE-2026-04-24.md(48308 bytes, md5 89f6ec13a09fd028ce2d29b618beb976) both exist post-reorg with divergent content. cli1's pw-api#215retargets references toarchive/status/; cli2's pw-os-v2#331retargets toarchive/state/. Net effect: pw-api and pw-os-v2 now point at different archive locations for the same logical file. Queued for follow-up content-fix dispatch (per Queue B instrategy/dispatch-next-iteration-queue.md) — pick canonical, delete non-canonical duplicate fromshared/, realign both consumer repos to one location. Cross-repo PR sequence: 1 PR in shared/ (dedupe) + 1 PR in pw-api (realign if non-status/ is canonical) + 1 PR in pw-os-v2 (realign if non-state/ is canonical). - Worker-launch-ritual
git add -Apattern flagged for v2 hardening. Both cli1 and cli2 staged viagit add -Aper the ritual's Phase 4 example; the global Claude Code CLAUDE.md cautions against-Ain favor of by-name staging (sensitive-file inclusion / large-binary risk). Not a live problem in either Phase 3 PR (diffs were entirely doc/path-substitution; no untracked sensitive files in the worktrees), but the ritual's example is the canonical worker reference and should be hardened to either by-name staging or an explicit "verified-clean-worktree" pre-check. - CTO/CISO sole approval. Path-canonical changes across four repos; no compliance content; no behavior change; routine cleanup chain.
2026-05-14 (post-reorg cross-repo cascade landed + dispatch-pattern hardening)
- Five-PR cross-repo cascade landed within the cascade iteration. The post-2026-05-14 tiered reorg (pw-shared #87 at d50b59d) propagated through all five active code/doc repos via sequenced cascade dispatches. PR ladder: pw-website #96 (a0013a1) → pw-api
#214(bad26ef) → pw-portal-v2#58(f8ca59a) → pw-os-v2#330(082df26) → pw-infrastructure#186(a575625). All Phase 2 NEWLY-BREAKING substitutions applied; ALREADY-BROKEN cleanup + operator-decision items deferred to follow-up work (queued atstrategy/dispatch-next-iteration-queue.md). Phase 4b runtime-concatenation sweep added after pw-website #96 surfaced a regex-blind spot atscripts/compliance-lint.mjs:242(join(SHARED, "privacy.md")slipped past the literal-path regex because the literal had noshared/prefix); zero hits across the subsequent four cascade repos, validating the sweep as confidence-building negative for an infra-heavy cascade. - Operational vehicles validated three times each: (a)
strategy/2026-05-15-cross-repo-cascade-prompts.mdas the authoritative move table (drafted earlier in the iteration, refined live as the pw-website learning surfaced); (b)dispatch/inbox-on-disk pattern (shipped same iteration at 9d38dfc) replacing chat-pasted prompts withpending.md+worker-launch-ritual.mdfor prompt-durability across display issues, lossy clipboards, and chat-rendering quirks; (c) parallel-worker discipline (cli1 sequencing pw-api → pw-portal-v2 in separate worktrees; cli2 alone on pw-os-v2; cli1 follow-up on pw-infrastructure) honoring single-agent-per-repo. - Same-iteration discipline hardening —
chore(dispatch): ritual hardening from cascade learningsat 9ba0e39. Five hardening additions todispatch/shared/worker-launch-ritual.md+dispatch/README.md, each traced to a specific cascade incident: fresh-CLI-session-per-task (cli1's reused-session refused to engage with the new pw-infrastructure dispatch), CWD-anchor-every-Bash (cli1's pre-flight surfacedShell cwd was reset to /mnt/c/Users/nickin WSL between Bash calls), auto-merge-enable-and-verify (cli2's pw-os-v2#330returnedautoMergeRequest: nullafter PR create and sat OPEN until operator intervention), completion-report-inline-in-archived-pending.md (all four worker archives were verbatim prompt text only; orchestrator had to reconstruct worker output from PR body + diff), done/-filename-status-suffix (<timestamp>-<status>.mdfor grep-scannable dispatch history). - Polling-loop v2 architecture queued at
strategy/dispatch-control-architecture.md(operator-review-gated design proposal, not implementation). Treats dispatch as a control-systems engineering problem: three nested control loops (inner 30s file-state polling; middle 5-min batch-progress monitoring; outer per-iteration pattern detection and setpoint adjustment); quantitative setpoints with measurable error functions per task class; stability mechanisms (rate-limited control actions, hysteresis on intervention thresholds, circuit breakers on autonomous retry loops); classical-control vs AI-driven distinction explicit; prior art from Kubernetes controllers, Netflix Spinnaker, adaptive control with divergence rationale where PW design departs. Six observed setpoints from this iteration's cascade captured for the setpoint table (auto-merge verification, Phase 1 baseline drift, Phase 4b yield, completion-report channel, wall-time bounds, session-reuse contamination). Design eliminates the first / fourth / fifth hardening additions structurally; second + third remain operational discipline regardless of dispatch generation. - Follow-up queue captured at
strategy/dispatch-next-iteration-queue.md(iteration-boundary handoff artifact, not date-stamped per state-based naming convention): Phase 3 ALREADY-BROKEN mechanical cleanups (4 repos), operator-decision content fixes (pw-os-v2PW-SECURITY-POSTURE-*glob +cdf.mdaspirational ref; pw-infrastructureopen-source-inventory+covered-ui-checklistlegacy refs),docs/canonical-references.mdrewrite PR (25 speculative refs requiring per-row filesystem-state audit, not mechanical sed-replace), weekend hygiene queue (pwos-core/nexus-core flags + pw-website doc-text refs + plaintext-credentials remediation), polling-loop v2 implementation queued behind architecture-doc review approval. - CTO/CISO sole approval. Path-canonical changes only across the cascade; no compliance content modified; no Marketing Rule / 17a-4 / Reg S-P surface touched. Cascade PRs auto-merged on green CI per repo conventions; pw-os-v2
#330required operator manual merge after cli2 missed the auto-merge enable step (gap closed by ritual hardening above).
2026-05-14 (evening — shared/ tiered reorg)
- refactor(shared): tiered reorganization landed.
docs/consolidation for canonical firm + compliance markdown (5 firm operating docs todocs/firm/; 16 compliance program documents todocs/compliance/);compliance/reg-s-p/signed-pdfs/split intodocs/pdfs/(4 current pre-sign drafts) +docs/signed/(DocuSign returns; empty) +docs/versions/<doc-base-name>/(3 prior-version drafts);AGENTS.mdmerged intoCLAUDE.mdas the new "Agent Coordination Protocol" section;llms.txtdeleted (zero cross-repo consumers verified across all eight repos in the active estate);shared/status/+shared/inbox/+architecture/api/fmp.mdarchived toarchive/;compliance/changelog.mdrenamed tocompliance/COMPLIANCE-CHANGELOG.md; CHANGELOG quarterly split policy documented atarchive/changelog/README.md(current quarter at root, older months to per-month archive files at quarter boundaries; no entries split yet since the cross-repo CHANGELOG started 2026-04-26). - Intra-shared reference cascade: 27 living markdown files updated via perl two-phase substitution (shared/-prefixed + bare-relative with negative lookbehind to prevent double-substitution) to point at the new
docs/firm/anddocs/compliance/paths. Pre-existing dangling references opportunistically fixed during the same cascade (SHARED/governance/disclosures.json→shared/disclosures.json;SHARED/governance/ria.md→shared/docs/firm/ria.md;shared/vault/tax-ref.md→shared/docs/firm/tax-ref.md;shared/PW-SECURITY-POSTURE-INTERNAL-v1.0.md→shared/docs/compliance/security-posture-partner.md). Dated audit-trail records (cco-approvals/,decisions/,vendor-correspondence/,protocols/,archive/, this CHANGELOG,COMPLIANCE-CHANGELOG.md) deliberately left unchanged per audit-trail discipline (state-as-of-capture-date). - Status-doc compile workflow retired (
skills/session-end-hygiene/SKILL.mdStep 3 marked RETIRED 2026-05-14): the PW-STATUS-AND-CAPABILITIES daily compile is superseded by live state anchors —strategy/CURRENT-STATE.md(live firm state),shared/CHANGELOG.md(cross-repo PR ledger, append-on-merge), per-repo CHANGELOGs (repo-level activity),docs/compliance/governance-dashboard.md+ciso-dashboard.md(pending sign-offs), andpwos.app/workstreams(partner-tier action items). Historical PW-STATUS-AND-CAPABILITIES snapshots remain inarchive/status/as frozen state-of-the-firm captures; new compiles are no longer produced. - ~36 newly-breaking cross-repo references queued for follow-up cascade PRs in
pw-api,pw-os-v2,pw-portal-v2,pw-infrastructure, andpw-website(planned 2026-05-15). These reference the pre-reorg paths (shared/AGENTS.md×9,shared/privacy.md×4,shared/compliance/wisp-compliance-hub-plan-2026-04-29.md×5,shared/compliance/newsletter-tags-taxonomy.md×3, plusshared/inbox/*×7 and others). Per the reorg out-of-scope clause, these stay out of this PR; consuming repos cascade separately. - Targeted fixes alongside the reorg:
compliance/audits/blockskunk-phase-0-input-package.md:140— dangling reference to non-existentshared/compliance/posture/ciso-appointment-letter-2025-12-31.mdcorrected to the actual canonical fileshared/governance/CISO Appointment - CTO/CISO.pdf.docs/firm/terminology-map.md— three forward-references to unbuilt artifacts (SHARED/strategy/copy.json,vision.md,roadmap.md) annotated inline as "(planned; not yet authored)" to preserve intent visibility while clearly marking not-yet-implemented.
- Positive finding worth noting: the rename targets
privacy-policy.mdandterms-of-service.mdwere ALREADY cited as canonical inreg-s-p-compliance-record-2026.mdlines 186-187 prior to this reorg. The compliance record had documented these paths as canonical; this reorg realizes the documented intent. - CTO/CISO sole approval. No CCO sign-off required — path moves only; no content changes to compliance documents. CCO reviews the rendered compliance docs via DocuSign as the natural review surface. Reorg-only changes recorded in
compliance/COMPLIANCE-CHANGELOG.mdby Rule 204-2 obligation: every path-canonical change to a compliance document is logged there even when the document's content is unchanged. - PR open for manual merge (auto-merge disabled per CTO/CISO directive on structural-change PRs); Gate C is CTO/CISO review.
2026-05-14 (evening — Chainalysis + Zapper removed from compliance docs)
- docs(shared): pre-DocuSign-send vendor reference correction across the compliance document set. PW does not use Chainalysis and does not use Zapper; three CCO-signed PDFs (KYC/AML Policy, Reg S-P Compliance Record, Customer Breach Notification Template) and their cross-doc dependencies (Subprocessors Inventory, partner Security Posture, BlockSkunk audit input, AML/OFAC gap map) carried stale vendor references that CTO/CISO caught pre-send. Single PR substitutes the actual stack: Veriff for TradFi KYC + onboarding sanctions, Scorechain (Free Sanctions API for OFAC/SDN + Risk Assessment API via QuickNode for chain-scoped KYT/KYW on BTC/ETH/SOL/Base/XRP/AVAX) for onchain compliance, DeBank primary + Octav backup for onchain position aggregation; manual CCO review with OpenSanctions + public block-explorer tools is the documented fallback for activity on chains outside Scorechain's supported set.
- Version bumps: KYC/AML Policy v1.0 → v1.0.1; Reg S-P Compliance Record v2026.0.1 → v2026.0.2; Subprocessors Inventory v1.1 → v1.2 (in-content; filename retained as
pw-subprocessors-v1_1.mdto avoid cascading cross-reference breaks); Customer Breach Notification Template unchanged at v0.3 (no Chainalysis references found; per task conditional, leave unchanged). Partner Security Posture v1.1 + gap map + BlockSkunk audit input edited in place. - PDF regeneration:
kyc-aml-policy-v1_0_1.pdf+reg-s-p-compliance-record-2026-v2026_0_2.pdfrendered via the existing pandoc + xelatex pipeline (no--signatureflag — both docs have native sign-off blocks); landed incompliance/reg-s-p/signed-pdfs/. Previous v1.0 / v2026.0.1 PDFs preserved as historical artifacts. - Q1 (Scorechain capability surface) and Q2 (onchain vendor verification) resolved by CTO/CISO mid-PR: Q1 → use exact product names from
architecture/api/scorechain.md(Free Sanctions API + Risk Assessment API via QuickNode); Q2 → DeBank confirmed primary viapw-api/src/lib/debank.ts, Octav added as backup per Secret Manager credential bindings, Zerion removed (no integration, no credential), Zapper removed (no integration, no credential). Three "Scorechain entity-type" rewrites in KYC/AML Policy §8 carry inline TODO comments flagging the specific capability claims for CTO/CISO final-pass review before sign-off. - CTO/CISO + CCO sole approval (observability + policy-text correction; no posture change requiring CCO pre-sign on AML obligations or Reg S-P controls). Compliance posture-corrective only — CCO reviews the corrected PDFs via DocuSign as the natural review surface.
2026-05-14 (afternoon — three terraform applies cleared)
- infra(pw-infrastructure): three terraform workspaces applied in order.
cloud-run-baseline→monitoring→secrets. End-state: all four Cloud Run services pinned at min=1/max=2 exceptpw-api-webhooks(intentional 1/5 for Quiltt burst capacity); pw-portal carriesANTHROPIC_WORKSPACE_ID; egress-canary metrics + alerts ENABLED in prod; no remaining state drift. - cloud-run-baseline: first apply attempt at 15:30Z failed on
pw_portalwhile the broken revision was still unresolved (pre-#185 merge). Second apply ran cleanly post-#185 (15:44Z window). Plan output read oddly because Terraform 6.x normalizes scaling blocks differently than the prior provider —min_instance_count = 0 → nulllines surfaced on all four services, but those were cosmetic provider-version noise on the already-pinned pw_os/pw_api_webhooks; the real change was the pw_portal env add plus pw_api/pw_portal scaling codification per#185. - monitoring: clean apply; 2 dashboards re-serialized (cosmetic JSON normalization —
etagfield removed and xPos/yPos field reordering).gcloud alpha monitoring policies listconfirmed bothpii_egress_canary_blocked+pii_egress_detector_mismatchalerts are ENABLED in prod. Worth noting for the record:#183's metrics + alerts were already in state when this workspace applied — a prior CI auto-apply earlier in the day had absorbed them before this orchestrator session noticed. Re-apply was a no-op for those resources. - secrets:
No changes. Your infrastructure matches the configuration.The Finnhub key removal from#182had likewise been absorbed at the prior auto-apply. Workspace in sync. - Local toolchain side-notes (not prod, captured for follow-up):
gcloud alpha monitoringrequired a component install mid-session (didn't have alpha installed locally). operator's gcloud bundled Python threwruamel.yamlandoauth2clientImportErrors during the live-service-state describe loop — cosmetic broken-dependency on the local Cloud SDK install, not a prod issue. Recommended fix later: reinstall gcloud or update components. - CTO/CISO sole approval. Apply-only entry; the underlying PRs (
#182/#183/ #185) carried their own approvals.
2026-05-14 (afternoon — pw-portal deploy incident + closure — pw-infrastructure #185)
- fix(pw-infrastructure): closure of the pw-portal startup-assertion incident; same PR codifies a pre-existing scaling drift. pw-portal-v2
#57(merged 14:14Z) addedapps/api/src/lib/startup-assertions.ts, a boot-time guard that callsprocess.exit(1)in production whenANTHROPIC_WORKSPACE_IDis unset. The pw-portal Cloud Run service had no such env var; revisionpw-portal-00067-w5pauto-deployed at 14:16Z, the container booted, printedpw-portal-v2 API starting on port 8080, then loggedrefusing to serve trafficand exited 1. Cloud Run held 100% traffic on prior healthy revisionpw-portal-00065-gtf(which predates the assertion code and so was unaffected). The public endpoint stayed 200 throughout; no client impact at any point. - Diagnosis (~10 min total): (1)
gcloud run services describe pw-portalshowedlatestReadyRevision=00065, not00067. (2)gcloud run revisions logs pw-portal-00067-w5pshowed the boot banner followed byrefusing to serve traffic. (3)Readofapps/api/src/lib/startup-assertions.tsnamed the missing env var. Three steps, no false starts. - Fix (pw-infrastructure
#185, merged 15:43Z):- Adds
ANTHROPIC_WORKSPACE_IDenv block on thepw_portalCloud Run service interraform/cloud-run-baseline/cloud-run-services.tf— literalfe30b317-0a9c-47df-a972-b9611e6e0002, mirroring the existingpw_osblock (one ZDR workspace, both services). - Same-PR scope addition: pins scaling explicitly on
pw_api(was 0/10) andpw_portal(was 0/10) tomin_instance_count = 1/max_instance_count = 2, codifying the previously-live state from pw-infrastructure#178. Closes themin_instance_count = 0 → nullapply-drift that the workspace's plan output had been carrying on all four services.pw_osalready correctly pinned (1/2);pw_api_webhooksintentionally left at 1/5 as the legitimate-difference exception (Quiltt 20s response-window burst capacity per existing inline rationale comment) — called out in PR body.
- Adds
- Closure: terraform apply post-merge created
pw-portal-00068-kdgwith the env var set; revision went Ready, took 100% traffic. The boot assertion implicitly passed (the assertion exits on failure, so Ready + 200 response is the behavioral proof). Incident closed at 15:44Z. - Why this is a finding, not just a fix: auto-merge has no post-deploy health gate. A PR that adds a
process.exit(1)boot requirement should not be able to silently ship a broken revision and only get caught because we happened to touch terraform next. Captured as a 2026-05-14 process finding instrategy/2026-05-14-process-findings.mdalong with three other lessons from today's session. - CTO/CISO sole approval. Same-day incident fix; doc-only PR body discussion of the scaling pin (no service got unpinned).
2026-05-14 (afternoon — egress canary metrics + alerts + runbooks live in prod — pw-infrastructure #183 + #184)
- infra(pw-infrastructure): Cloud Logging log-based metrics + alert policies + runbooks for the PII egress canary now end-to-end in prod. Closes the P1-ish follow-up flagged in this morning's pw-os-v2
#329+ pw-portal-v2#57entry. Application-side canary already aborted any Anthropic-SDK egress carrying residual PII; this PR pair lights up the alerting + operator-response surface. - pw-infrastructure
#183(merged 15:29Z): two log-based metrics onjsonPayload.action="pii_egress_canary.blocked"and onjsonPayload.action="pii_egress.request"with the field-to-field filterjsonPayload.turns_scanned < jsonPayload.turns_in_request(honest-metrics drift signal — surfaces when a user-role turn reached the SDK egress unscanned). Two alert policies wired to the existinggchat_alertsnotification channel, 300s evaluation window, 7200sauto_close. Field-to-field comparison lives in the metric filter, not the alert threshold (Cloud Monitoring alert conditions can't express field-to-field; the filter approach surfaces the violation at metric-ingest time and the alert is a simple "metric ticked > 0" gate).terraform/monitoring/dashboards.tf+alert-policies.tf, 229 insertions. - pw-infrastructure
#184(merged 15:35Z): two operator-facing incident runbooks atpw-infrastructure/docs/runbooks/pii-egress-canary-blocked.md+.../pii-egress-detector-mismatch.md. Reg S-P §248.30(b) framing — every canary block is a missed scrub by the upstream Contract#3PII guard, so filing a remediation ticket is non-optional regardless of root cause. Path correction worth flagging: the original task brief saidshared/runbooks/, but#183's alertdocumentation.contentblocks link tohttps://github.com/Protocol-Wealth/pw-infrastructure/blob/main/docs/runbooks/...; the CLI agent correctly verified against the live URL prefix and placed the runbooks where the alert triage links resolve.pw-infrastructure/docs/runbooks/README.mdindex updated with both entries. - Confirmed ENABLED in prod via
gcloud alpha monitoring policies list --filter='displayName:pii_egress'after the monitoring workspace apply:pii_egress_canary_blockedENABLED,pii_egress_detector_mismatchENABLED, both targetinggchat_alerts. Defense-in-depth posture is end-to-end: application canary aborts on residual PII → structured log → metric → alert → operator runbook → CCO loop-in. - CTO/CISO sole approval. Infra-only; no business-logic change; security-posture strengthening.
2026-05-14 (independent PII egress canary — pw-os-v2 #329 + pw-portal-v2 #57)
- feat(pw-os-v2, pw-portal-v2): independent PII egress canary as defense-in-depth Reg S-P §248.30(b) safeguard. Adds a standalone egress-time check on every Anthropic-SDK outbound call: if the raw request body matches an independently-implemented PII pattern set, throw
PIIEgressCanaryErrorand surface aspii_blockedfailure class without leaving the process. This sits behind the existing PII redaction (Contract#3pii.ts) and exists specifically to catch a future regression where the upstream scrubber silently drifts — ifpii.tsever misses a class, the canary still does. Pattern set is deliberately re-implemented from scratch (nopii.tsimport) so the two layers cannot share a bug. A shared module would defeat the design and is acknowledged as future work; the byte-identical field-name set across the two canary copies is pinned by field-name-pin unit tests in each repo (cross-repo contract). - pw-os-v2 (
feat/pii-egress-canary, #329): newapps/api/src/lib/pii-egress-canary.tsstandalone module. Wired at 5 SDK egress sites:claude-client.ts(chat stream),workstreams/summarizer.ts(sync + streaming),compliance/analyze.ts,social-drafts.ts.chat.tspassespiiMetricswith honestturnsScanned(incremented at every actualscan()call site).observability.tsclassifyFailurerecognizesPIIEgressCanaryError→pii_blockedfailure class. 24 new tests + 3 modified; 1116 total passing. - pw-portal-v2 (
feat/pii-egress-canary, #57): egress canary mirror (moduloREPO_TAG). Wired at the raw-fetch egress inanthropic-client.ts:streamMessage.chat.tspassespiiMetrics; multi-turn test agrees with the existing AX.2 contract test.PIIEgressCanaryErrorgets a dedicated SSE branch (kind='pii_egress_blocked'). 18 new tests; 105 total passing. - pw-api correctly skipped. pw-api makes ZERO Anthropic-SDK calls (no
@anthropic-ai/sdkimport anywhere); it is the vendor-data bridge only (FRED / MBOUM / EDGAR / Marketstack / Tradier / Brave / DeFiLlama / Wealthbox / etc.).pw-api/src/config/claude.tsis model-ID constants for downstream consumers, not a client. Anthropic-calling repos are pw-os-v2 + pw-portal-v2 only. - Cross-repo contract: egress canary field-name set is byte-identical across pw-os-v2 and pw-portal-v2, pinned by field-name-pin unit tests in each repo. Blocked-row schema identical save the
REPO_TAG. Two in-sync copies, deliberately not a shared module — the independence is the point. - Handoff (follow-on, NOT in these PRs): pw-infrastructure Cloud Logging log-based metric + alert on
pii_egress_canary.blockedevents and onturns_scanned < turns_in_request(an honest-metrics drift signal). Tracked as a P1 roadmap follow-up. - Closes: AP0.3 / Section A defense-in-depth follow-on from the overnight audit (PII guard had no egress-time backstop; now does).
- CTO/CISO sole approval. Security-posture strengthening; no business-logic change to Claude responses; no client data touched in the change itself.
2026-05-14 (terraform Finnhub dead-vendor removal — pw-infrastructure #182)
- chore(pw-infrastructure): remove Finnhub terraform residue. Finnhub was decommissioned from the pw-api scan-bundle stack 2026-05-13 (pw-api
#206/ FMP+Finnhub restructure). The application-side removal landed cleanly; the terraform side carried three dead Finnhub artifacts forward:FINNHUB_API_KEYenv-var block incloud-run-baseline/,finnhub-api-keyentry in theapp-deployers/secret map, and 3 Finnhub rows in thesecrets/README. This PR removes all three (3 files changed, 8 deletions).fmt+validateclean acrosssecrets/,cloud-run-baseline/, andapp-deployers/. - NOT a continuation of
#181. pw-infrastructure#181already finished the FMP terraform removal cleanly (env-var block, secret-map entry, README rows, andgcloudexample all removed in #181); the FMP audit-finding AP0.7 closed in#181+ pw-apichore/remove-fmp-integration. This PR is the Finnhub dead-vendor cleanup, parallel to but separate from the FMP work. No remaining FMP residue exists post-#181. - Apply order (manual, post-merge; WIF + state lock):
cloud-run-baseline+app-deployersfirst, thensecretslast. CTO/CISO applies; serialize perfeedback_serialize_parallel_infra_merges. - Out of scope / flagged for follow-up: dead
fmp:*Redis-namespace example interraform/monitoring/alert-policies.tf:260(cosmetic; carries forward from #181); consumer-attribution drift interraform/secrets/README.md(pw-nexus vs pw-api on ~6 vendor rows; pw-nexus retirement cleanup). Both tracked on the roadmap, not this PR. - CTO/CISO sole approval. Dead-vendor cleanup; no live service references; terraform-only.
2026-05-14 (scan-bundle profile chain restored — MBOUM primary, EDGAR-SIC last resort)
- feat(pw-api, pw-portal-v2): scan-bundle sector / industry / description restored. The FMP teardown (
chore/remove-fmp-integration, merged earlier today) left/scan-bundleStep 2 null on those fields with a roadmap-follow-up comment. This PR pair fills them in: pw-api wires MBOUMmodule=profileas primary (Yahoo-mirroring shape, ~99% US-listed coverage, included in the Business tier we already pay for; never wired previously due to a stale "MBOUM has no profile shape" comment that turned out to be wrong) → EDGAR submissionssicDescription+ a lazy 2-digit-SIC-prefixsicToSector()table as last resort (description stays null on this branch — EDGAR carries no longBusinessSummary equivalent). ETFs the classifier short-circuits produceprofile_source=null(no useful sector mapping for funds). - pw-api (
feat/scan-bundle-profile-mboum-primary): newsrc/lib/mboum.tsfetchMboumProfile(Zod-schema'd against the inferred response shape — schema drift throwsMboumAPIError(upstream)so the first real call surfaces a mismatch loud; the endpoint was NOT exercised live during the FMP-removal inventory). Newsrc/lib/sic-to-sector.tswith ~30 seeded prefixes (lazy growth). Newsrc/routes/scan-data.tsStep 2 wires the chain; newScanBundleResponse.profile_source: 'mboum' | 'edgar_sic' | nullsignal +scan_bundle.profile_resolvedper-ticker structured log. New narrow endpointGET /v1/internal/market/profile/:symbol(same chain) for consumers that don't need the full scan-bundle. 21 new unit tests across 3 files (mboum-profile.spec.ts/sic-to-sector.spec.ts/scan-data-profile.spec.ts); 818 total pass; lint clean. AlphaVantage deliberately NOT wired as a fallback (separate decision, out of scope). - pw-portal-v2 (
fix/markets-profile-repoint):apps/api/src/routes/markets.tsproxies the restored chain via the narrow/v1/internal/market/profile/:symbolendpoint, replacing the removed/v1/internal/market/fmp/profile. Markets tile field mapping adapted to the new response shape;CLAUDE.mddoc drift ("quote + FMP company profile + MBOUM news") fixed. Tile degrades gracefully for symbols where both MBOUM and EDGAR-SIC miss (empty profile section, no crash — existing missing-profile path). - Why now: the FMP teardown opened a sector-signal gap in pw-os-v2's 7-layer durability model. layer_classifier.ts gracefully tolerated empty-string sectors (
|| ''guards), but the EMF scoring path ran without sector signal — fine as a temporary degradation, not as a permanent state. MBOUM module=profile was always the right primary; it just wasn't wired. Cost: zero — already paying for the Business tier. - shared/architecture/api/mboum.md — adds the inferred response shape for
module=profile(body.assetProfile.{sector, industry, longBusinessSummary, country, website}) flagged as INFERRED-not-exercised-live until the post-deploy first-call validation confirms the shape. - Deploy gate (manual, post-deploy): first-real-call validation against a US large-cap; confirm
profile_source='mboum'with non-null sector/industry. Schema drift will throw clean and fall to EDGAR-SIC; check Cloud Logging formboum.profile.schema_mismatch. - Closes: the sector-signal gap in pw-os-v2's 7-layer durability model. The cross-repo follow-up flagged by
chore/remove-fmp-integration(pw-portal-v2 markets tile dangling proxy). - CTO/CISO sole approval. Internal data-pipeline restoration; no client data; paid + free ToS-clean sources.
2026-05-14 (FMP integration removed entirely — closes audit AP0.7)
- chore(pw-api, pw-os-v2): remove FMP (Financial Modeling Prep) integration entirely. FMP was flag-off in prod since 2026-05-13 (PR #204) and fully superseded by the MBOUM Business primary + Marketstack Basic price secondary + SEC EDGAR XBRL fundamentals canonical + Twelve Data quaternary chain. The retained API key and dead helper code were the only thing keeping the audit finding AP0.7 (FMP free-tier ToS §2.2.1 commercial-use exposure) live. This PR rips the integration out across both repos.
- pw-api (
chore/remove-fmp-integration): deletedsrc/lib/fmp.ts(765 LOC FMP client lib); deleteddocs/integrations/fmp.md; deleted 3 FMP-centric test files (test/unit/cache-policy.spec.ts,test/unit/market-data-expansion.spec.ts,test/unit/scan-data.spec.ts,test/unit/scan-data-fmp-disabled.spec.ts). Removed the 8/v1/internal/market/fmp/*routes fromsrc/routes/internal.ts(profile / income-statement / balance-sheet / cash-flow / key-metrics / etf-holdings / etf-sectors / etf-countries / insider-trading). RemovedFMP_API_KEY+FMP_FALLBACK_ENABLEDfromsrc/config/env.ts. Removed FMP fromsrc/routes/scan-data.ts— imports +fmpStatementsToScanBundle,fmpErrorCode,runFmpFallbackCallhelpers + Step 2 profile fetch + Step 6 FMP legacy fallback +'fmp'fromdata_source/providers_attempted/error_codesunions + FMP branches in winner logic + FMP in error-code coalesce + unusednumOrNullhelper. Removed FMP TTL row fromsrc/lib/cache/cache-policy.tsand'fmp:'namespace from cache-bust allowlist. Surgically pruned 11 incidentally-FMP-referencing test files (env vars, mock fns, response shape types). UpdatedAGENTS.md+README.md+CLAUDE.mdprovider-chain docs. - pw-os-v2 (
chore/remove-fmp-integration): deleted 4 FMP-only chat-tool bridges (get-company-financials.ts,get-company-metrics.ts,get-etf-breakdown.ts,get-insider-trading.ts); excised FMP hop from multi-vendorget-quote.ts(cascade now MBOUM→Tradier; tool remains functional); excisedget_company_profilefrommarket-quotes.ts(kept MBOUM/Tradier/DeFiLlama tools). Removed 4 deleted-bridge imports fromtools/index.ts. Removed'fmp'fromScanError.providerunion +Fundamentals.dataSource+scoringProvider; wideneddataSource/scoringProviderto include'edgar'/'twelvedata'/'mboum_thin_etf'(mirror of pw-api widening). RemovedproviderFromBundleCode()fmp_branch fromscan_runner.ts. Removedfmp_fallback_invocations/fmp_fallback_successes/fmp_fallback_failures/fmp_thinapiCallCounts entries. Restructured ScanError attribution (mboumAttempted && otherAttempted → 'multi'). Removed FMP vendor row fromseed-vendors.json. Updated chat-tool count discussion in CLAUDE.md (53 → 48). Test fixtures retargeted from FMP to EDGAR / MBOUM where possible. - Behavior changes (deliberate; called out for examiners):
- scan-bundle
sector/industry/description/market_capnow resolvenull. FMP profile fetch was unconditional (Step 2) and is removed with no in-PR replacement. EDGAR-SIC sector mapping is tracked as a P1 roadmap follow-up. pw-os-v2apps/api/src/lib/scanning/layer_classifier.tsdegrades gracefully (empty-string guards at:53-54); the EMF durability model runs without sector signal until the EDGAR-SIC follow-up lands. (Reverted by the profile-restoration PR above, 2026-05-14.) get-quote.tscascade reduced from MBOUM→FMP→Tradier to MBOUM→Tradier. Tool stays registered. Both surviving paths verified passing via the existing unit test (apps/api/src/lib/tools/bridges/get-quote.test.ts).
- scan-bundle
- Cross-repo follow-up (NOT in this PR — flagged for CTO/CISO):
pw-portal-v2/apps/api/src/routes/markets.ts:158proxies the removed/v1/internal/market/fmp/profileendpoint;MarketsTile.tsxwill render empty profile sections for all symbols post-merge until pw-portal-v2 drops the call or repoints to the EDGAR-SIC follow-up. Per task scope ("pw-portal-v2 not in scope — report, don't fix"). UX impact: degraded display, not crash (existing code anticipates "many ETFs / OTC names don't have an FMP profile"). (Resolved by the profile-restoration PR above, 2026-05-14.) - Deploy-gate step (manual): CTO/CISO removes
FMP_API_KEYfrom Secret Manager (pwllc-fmp-api-key) post-merge. Not a code step; not in this PR. - Closes: pw-api
#180(FMP /stable/etf-*404 — moot, integration removed). pw-api#186/#187(scan-data error_code disambiguation — superseded; per-providererror_codes: {mboum, marketstack, edgar, twelvedata}shipped with syntheticboth_providers_thinretained). Audit finding AP0.7 (FMP free-tier commercial-use exposure). - CTO/CISO sole approval. Dead-code removal; no client data; no behavior change to live provider chain. 797 pw-api unit tests pass; 1084 pw-os-v2 api unit tests pass; lint clean both repos.
2026-05-14 (public-repo honesty posture — nexus-core + pwos-core disclaimers; PWOS.md §8.1 inspected)
- docs(nexus-core, pwos-core): public-repo honesty disclaimer. Both public READMEs gain a
## Statusblock before any capability description: "This is a reference framework and a starting point, not a production-ready product. It is a work in progress under active, iterative development. Adopters are responsible for adding their own PII controls, access control, input validation, authentication, and data-handling boundaries appropriate to their own regulatory and security context before any real or sensitive data touches it. Adopters are also responsible for their own AI-provider data-handling posture; the framework makes no data-retention guarantees. Provided as-is under Apache-2.0." No ZDR / US-inference language was added to the public repos: ZDR is an account-level configuration of PW's own Anthropic workspace, not a property of the framework code; stating or implying it is built in would be the same false-claim error this PR exists to fix. - docs(nexus-core): claim reconciliation per AP1.12 (overnight audit finding).
README.mdarchitecture diagram lines "Multi-tier access control" + "Transport-layer PII filtering" under MCP Tool Registry replaced with "Pluggable ResponseFilter hooks (adopter-supplied auth / PII / audit)" — honest match to the actual code atsrc/nexus_core/mcp/server/app.pywhich exposes afilters: list[ResponseFilter]hook surface and ships no auth / tier / PII filtering of its own.docs/ARCHITECTURE.mdMCP Tool Pattern code example rewritten (prior example called acheck_tier(...)function that does not exist in the codebase); "Tiered Access" table (PUBLIC / USER / CLIENT / ADVISOR) replaced with an "Access Control and Tiering (Adopter-Supplied)" section that states plainly the framework does not enforce access tiers and the scaffold treats all callers as trusted. - docs(shared):
architecture/PWOS.md§8.1 inspected; no change. The "Anthropic API under a Zero Data Retention workspace, US-only inference" posture is already stated plainly in §8.1: workspace UUIDfe30b317-0a9c-47df-a972-b9611e6e0002, contractual non-retention, training exclusion, and "Geographic restriction: inference restricted to US-based infrastructure" all explicit. No edit warranted; no §18.1 revision-history bump. - CTO/CISO sole approval. Doc-only across three repos; public-repo content is regulatory-adjacent (Marketing Rule §206(4)-1 false-claim avoidance) and the change strictly tightens, not loosens. No code, no migration, no posture change.
2026-05-14 (overnight state-doc reconciliation — CURRENT-STATE rewrite + ROADMAP.md seed)
- docs(shared): rewrite
strategy/CURRENT-STATE.mdagainst the live merged-PR ledger for 2026-05-11 through 2026-05-14; seedROADMAP.mdat repo root. Prior CURRENT-STATE was anchored 2026-05-14 EOD but still carried the superseded PR 8 5-bug ledger and the obsolete vendor stack section (MBOUM primary + FMP fallback). Reconciled: (a) Bug#2+ Bug#5closed pre-cron on 2026-05-11; Bug#3(FMP fallback chain) is moot — FMP flag-off in prod 2026-05-13 and Finnhub removed entirely; Bug#4hypothesis-rejected; Bug#1(worker singleton) effectively retired by PR 8.5 scheduler-tick handler (pw-os-v2#314, 2026-05-12) — the worker singleton no longer exists. 2026-05-12 09:00 UTC cron outcome left[unverified](gcloud non-interactive in this session; deferred to interactive verification). (b) Vendor stack rewritten: MBOUM primary, Marketstack secondary, SEC EDGAR XBRL canonical fundamentals fallback, Twelve Data flag-off in prod, FMP flag-off in prod, Finnhub removed. (c) 2026-05-13 + 2026-05-14 PR waves enumerated againstgh pr list --state mergedper repo. (d) Compliance posture section reflects the three CCO-signing-queue PDFs (KYC/AML v1.0 → Customer Breach Notification Template v0.3 → Reg S-P Compliance Record 2026.0.1) and the BlockSkunk Phase 0/1 SOW signed. - New:
shared/ROADMAP.md— firm-level planned-improvements register. P0 / P1 / P2 / P3 sections. Each item: what / repo / effort (S/M/L) / gating (CTO-CISO-autonomous / CCO / CIO / external). Seeded from: open issues per repo; 2026-05-13 investment-meeting action items; BlockSkunk Phase 0 dependency chain; Reg S-P June 3 deadline items; deferred work (caching layer Phase 1; ZONAL apply window; C5 cleanup PRs; Langfuse cache TTL observation). Reserved "## Phase 2 — Audit Findings" section at the bottom for an out-of-band overnight read-only audit append. - CTO/CISO sole approval. Doc-only; no posture change; no business logic.
2026-05-13 (fundamentals-chain hardening — Finnhub removed; FMP + Twelve Data flag-off; ScanError diagnostic fields)
- chore(pw-api
#203/ #204): structured logs on every FMP fallback HTTP call +FMP_FALLBACK_ENABLED=falseset on Cloud Run. Closes the FMP free-tier 429-exhaustion class-of-bug surfaced 2026-05-11 (50 alleged "MBOUM misses" actually masked FMP's 250 req/day free-tier cap). Observability first; flag-off second. Code path retained behind the flag in case of a future paid-tier upgrade decision. - feat(pw-api #205): Twelve Data wired as MBOUM secondary fallback (gated). Initial drop-in; validated 2026-05-13 that Twelve Data free Basic tier does NOT include fundamentals endpoints (403 across
/income_statement//balance_sheet//cash_flow).TWELVEDATA_FALLBACK_ENABLED=falseset on Cloud Run; code retained. - chore(pw-api #206): Finnhub removed entirely from the scan-bundle stack; Marketstack added as commercial-licensed secondary. Net effect: Finnhub vendor decommissioned (removed from
shared/architecture/api/index; no consumers remain in pw-api). Marketstack inserted as Step 3 (price-only) ahead of the EDGAR XBRL fundamentals step that landed in#208. - fix(pw-os-v2 #319):
ScanErrorenvelope populates diagnostic fields (http_status,error_code,provider) on every scan-runner failure path + corrupt-payload guard. Companion to pw-api's per-providererror_codesfield shipped in pw-api#186/#188. Renames pw-os-v2's enum'both'→'multi'in subsequent#321(already CHANGELOG'd) to accommodate ≥5 providers. - Net 2026-05-13 stack state: ~$44/mo paid vendor spend (MBOUM primary only); EDGAR + Marketstack are zero-cost; FMP + Twelve Data flag-off; Finnhub removed. Full chain detail: see entry "2026-05-13 (pw-api EDGAR XBRL fundamentals fallback)" below.
2026-05-14 (PWOS.md v1.2 + canonical-path move + orientation-doc stale-path sweep)
- docs(shared): PWOS.md v1.2 + canonical-path move + orientation-doc stale-path sweep. PWOS.md moved to its declared canonical path
shared/architecture/PWOS.md(was at root pending the move per v1.1 §18 footer). §9.1 retitled "The Claude orchestration pattern (two-to-six streams)" with a rewritten opener reflecting current 2-to-6 stream practice; Layer 1 / Layer 2 paragraphs retained verbatim as the foundational shape (the "floor" the multi-stream model composes from). §9.7 adds an OS-licensing linkage bullet pointing to §15.1 +strategy/multi-agent-coordination.md. §18.1 v1.2 revision entry. Header version bumped 1.1 → 1.2; effective date 2026-05-14; canonical-path footer cleaned (no longer "(move pending)"). - Bidirectional cross-reference added to
strategy/multi-agent-coordination.mdat the bottom (above the maintainer footer) pointing back to PWOS.md §9.1 / §9.4 / §9.7. Closes the navigation loop opened in v1.1. - Stale-path sweep of three root orientation docs (AGENTS.md, PW-CHAT-CAPABILITIES-AND-ROADMAP-2026-05-01.md, PW-ARCHITECTURE-DEEP-DIVE-2026-04-24.md). Per the brief, grepped each file first and only updated genuinely stale refs. Findings: PR
#70's earlier sweep had already closed the bulk; one residual stale ref in PW-CHAT-CAPABILITIES line 416 (shared/PW-STATUS-2026-05-01-EOD.mdnow atshared/archive/state/PW-STATUS-2026-05-01-EOD.md). PW-ARCHITECTURE-DEEP-DIVE retains itsshared/types/,shared/lib/,shared/audit/references because those are forward-looking design-intent paths (Contract#1/#3spec target locations, not stale post-reorg paths); leaving them aspirational matches the document's tone.strategy/cdf.mdlikewise retained as an aspirational draft path ("Phase 0 backtest first" gates the file's existence). - Scope expansion (documented): the PWOS.md move broke two references in
compliance/reg-s-p/reg-s-p-compliance-record-2026.md(§3 narrative + §4 source-of-truth table). Fixed both as a direct consequence of completing the move — half-finishing the move would leave a compliance doc dangling. Two surgical line edits, no content drift. The brief's step-6 scope guard ("touch only the three named orientation docs") was for the sweep, not for the move's own consequence fixes. - CTO/CISO sole approval. Doc-only PR; no posture change; same approval class as PR
#75.
2026-05-14 (firm-state EOD sync — source artifacts + nav doc updates)
- docs(shared): land the source artifacts referenced from PWOS.md v1.1 + today's PRs. PWOS.md (PR #68) cross-referenced four PDFs and one capture document that were intentionally left untracked at the time so a separate workstream could land them. That separate workstream is this PR.
- Governance PDFs (3, ~1.0 MB total):
governance/CISO Appointment - CTO/CISO.pdf— Unanimous Written Consent of Managers, dated 2025-12-31 (DocuSign envelope39704619-3610-4A29-8456-E227D97AFFAE). Appoints CTO/CISO as CISO with explicit authority over policy, incident response, asset protection, and legal coordination. Signed by CCO and CIO.governance/AML Appointment.pdf— Manager Resolution dated 2026-01-13 (DocuSign envelope9854DBBF-EB8C-47F1-8071-9D4063C65E47). (a) authorizes Wyoming → Delaware domicile transfer; (b) appoints CCO as AML Compliance Person; (c) renews CCO appointment; (d) confirms CISO designation; (e) appoints CTO; (f) appoints CIO. Signed by all three managers.governance/KYC-AML-Policy.pdf— Enclave Digital Wealth predecessor KYC/AML policy, effective 2025-03-01, signed by CCO as CCO 2025-04-16. Heritage record; superseded bycompliance/kyc-aml-policy-v1_0.md(PW-headed reissue from 2026-05-13, PR #64).
- Reference PDF:
reference/Protocol_Wealth_SOW_051126.pdf— signed BlockSkunk Inc. Phase 0 / Phase 1 Statement of Work, $5,000 total, 50/50 payment terms, ISO 27001 + SOC 2 alignment. Source artifact behind PWOS.md §13 andcompliance/audits/blockskunk-phase-0-input-package.md. - Strategy capture:
strategy/pwos-md-delta-2026-05-13-merged.md— the 9-section delta (369 lines) that became PWOS.md v1.1. Committed as a point-in-time capture document so future readers can diff intent vs. final wording; the canonical state lives inPWOS.md. - Vendor correspondence:
compliance/vendor-correspondence/anthropic-ciso-webinar-2026-05-12/screenshots/— two screenshots (~644 KB) from the Anthropic "Secure the Advantage: A CISO's Guide to Agentic AI" webinar attended 2026-05-12. Captured for the firm's vendor-engagement record. - Navigation doc updates (in the same commit so README + CLAUDE.md stay in sync per the "when a path changes, update both" rule):
README.md— directory tree now listsscripts/compliance/(markdown → PDF pipeline added in PR #63) as a top-level area;governance/description expanded ("signed Manager resolutions, officer appointment letters, predecessor policies; PDFs are 7-year retained artifacts of firm legal record");reference/description expanded to include vendor SOWs.CLAUDE.md— "Where artifacts belong" table grew two rows:scripts/compliance/(with runbook back-reference torunbooks/compliance-pdf-generation.md) andgovernance/(firm legal record).reference/row clarified to include vendor SOWs.
- Cleanup: removed two stale local files that were never canonical —
pwos-md-delta-2026-05-13.md(root) was an older draft superseded by the-mergedversion instrategy/;reference/blockskunk-phase-0-input-package.mdwas a byte-identical duplicate of the canonical atcompliance/audits/blockskunk-phase-0-input-package.md. - CTO/CISO sole approval. No business-logic or compliance-posture change — provisioning of source artifacts already referenced from canonical docs, plus navigation-doc updates that catch up to today's new directories.
2026-05-14 (demo surfaces: Calendly URL alignment)
- fix(pw-portal-v2): pwportal.app/demo now uses the same Calendly link as pwos.app/demo —
cxsk-7r5-qpm/protocol-wealth-investor(round-robin investor) →ct3f-pwd-qgm/protocol-wealth-team-discovery(team discovery). Per CTO/CISO 2026-05-13 follow-up to PR#51to keep both demo surfaces routing through the same scheduling link. PR pw-portal-v2#52(single-line CONSULT_URL constant change with comment documenting the retirement).
2026-05-13 (pwos.app/demo + pwportal.app/demo public surfaces shipped)
- feat(pw-os-v2 #324): pwos.app/demo public-facing showcase for OS-licensing prospects. New 8-section landing page with interactive EMF regime classifier, 8-check ticker selector (AAPL/MSFT/NVDA fixtures), chat IDE conversation replay with tool-call expand/collapse, append-only audit-log sample, 5-tier architecture cards, RIA-stack-vs-PWOS comparison table. Backend: 6 endpoints under
/demo/api/*(regime, regime/list, ticker, ticker/:symbol, chat/history, audit/sample) reading 6 hand-curated JSON fixtures fromapps/api/src/fixtures/demo/. Public + edge-cacheable (Cache-Control: public, max-age=60, s-maxage=300); router mounted before auth gates. All fixtures clearly fictional (Demo Advisor/[email protected]); audit-sanitization sweep test asserts zero@protocolwealthllc.comleaks. 1059 → 1070 api tests pass. - feat(pw-portal-v2 #50): pwportal.app/demo public-facing client showcase. Replaces the 2026-05-03 wireframe (PR #31) with a 7-section + sticky-banner client-prospect demo: portfolio dashboard (totals, allocation pie + bars, regime, advisor block), transparency (SVG line chart + benchmark table + itemized fee breakdown), 6 compliance protections with regulatory citations, 6-stage onboarding workflow with doc checklists, 5 communication channels. Cool-light theme (slate-50 page, white cards, brand teal primary) per
pwportal-cool-lightbrand spec. Backend: 3 endpoints under/demo/api/*(portfolio, performance, onboarding-checklist) reading 3 fixtures. Persistent sticky "Demonstration only. All data is fictional" banner. 65 → 73 api tests pass.
2026-05-13 (chat export to Google Docs from chat header)
- feat(pw-os-v2 #322): export advisor chat conversation to a Google Doc landing in PWOS_ADVISOR_DRAFTS_FOLDER_ID. CCO-prioritized at the 2026-05-13 investment meeting. New "Export" button in chat header → local preview modal → POST
/api/advisor/chat-export/google-docs→ DWD impersonation → Drive doc creation + body seed viadocsBatchUpdate. Alternative Pattern (dedicated POST endpoint, NOT synthetic chat turn): the local-preview button-click is the consent gate; no two-turn confirmation flow polluting the transcript. Audit row keyedresource_type='drive_file'/resource_id=<file_id>so SEC export filters can pivot uniformly with the existingdrive_create_docpattern. Newchat.export.createdaudit verb (3-segment registry shape; spec'schat_export.google_docs.createdwas canonicalized to fit the regex). Serializer atapps/api/src/lib/chat-export/serialize-to-markdown.ts: system messages excluded; PII placeholders preserved verbatim (export reflects what Claude saw); tool calls reconstructed fromaudit_logand folded into<details>blocks per assistant turn. 1033 → 1054 api tests pass (+21).
2026-05-13 (demo surfaces: pricing rewrite + Overview/Detailed toggle + Calendly links)
- docs(demo): refine pricing model + add Overview/Detailed mode toggle + correct Calendly links on both /demo surfaces. Pricing changed from "retainer + profit interest" to "service-based retainer + per-engagement scope-up" (cleaner advisor-facing framing; pwos.app/demo only — pwportal.app/demo had no profit-interest text and is a no-op). Overview/Detailed toggle added to both surfaces with localStorage persistence (default: Overview). Calendly links wired: pwos.app/demo to team-discovery URL (
ct3f-pwd-qgm); pwportal.app/demo to investor URL (cxsk-7r5-qpm; round-robin to an advisor). Toggle ARIA-labeled and keyboard-accessible; inactive variant is removed from the DOM (not CSS-hidden) so screen readers see only the active depth. Both PRs ship same-shape<ModeText overview="..." detailed="..." />helper. - PRs: pw-os-v2
#326(pwos.app/demo); pw-portal-v2#51(pwportal.app/demo).
2026-05-13 (pw-api Cloud Run image: pandoc + xelatex provisioning + /healthz/pdf)
- feat(pw-api): install pandoc + xelatex on Cloud Run image for future server-side compliance PDF rendering capability. Closes deploy gate flagged in PR
#325(pw-os-v2 chat exports chose pdfkit specifically to avoid this Dockerfile change). Addspandoc+texlive-xetex+texlive-fonts-recommended+texlive-latex-extra+fontconfig(pluswgetfor the existing HEALTHCHECK probe) to the runtime stage; estimated payload ~700MB, total image stays well under the 2GB ceiling.fc-cache -fvruns during build so the font cache is baked in. apt lists cleaned post-install. - Runtime base image change:
node:24-alpine→node:24-bookworm-slimacross all three stages (deps + build + runtime). Reason: the brief assumes Debian apt commands + Debian texlive packages, and the PW letterhead template (fontspec, Latin Modern,newunicodecharfor σ / 5σ in the breach-notification template) is well-supported on Debian texlive — Alpine's texlive is leaner and would need per-package spelunking with no compatibility guarantee. All three stages on the same libc keeps native-binding dependencies (pg, etc.) consistent. The Trivy + npm/npx-stripping discipline carries over unchanged (Debian slim puts npm at the same paths). - New endpoint:
GET /healthz/pdfon the pw-api service (api mode only). Returns{ status, pandoc: { available, version, error }, xelatex: { available, version, error }, tools_available }. 200 when both tools resolve; 503 when either is missing. Result cached in-process for 5 minutes so deploy smoke + ops curl don't fork xelatex on every hit. Implementation atsrc/routes/healthz-pdf.ts(~110 LOC + 5 unit tests covering both-available / pandoc-missing / xelatex-missing / cache-hit / first-line-version-parsing). - Runbook update:
shared/runbooks/compliance-pdf-generation.mdv0.2 — new §8 "Cloud Run availability" documenting that the toolchain is installed but no endpoint invokes it yet; the future CCO-self-servicePOST /api/advisor/compliance/regenerate-pdfendpoint is sketched with auth + audit + allowlist + output-folder constraints but not yet built. Also explicitly notes why this is separate from the chat-export pdfkit path (different requirements: deterministic in-request rendering vs. full LaTeX letterhead). - No new business logic: this PR only provisions infrastructure capability. No new PDF endpoint is exposed; no compliance document is regenerated server-side; no chat-export flow changes. Endpoint design + auth posture for the future regeneration endpoint is a separate work item.
- Deploy verification: post-merge, CI's deploy smoke step should hit
GET /healthz/pdfand confirm 200 with valid pandoc + xelatex version strings. Cold-start latency target: <2s impact from the new layer (mostly Debian glibc swap; the apt-installed binaries are only forked when /healthz/pdf is hit, not at boot). - Full pw-api unit suite green post-change: 850 → 855 (+5 healthz-pdf tests). Lint + typecheck clean.
- CTO/CISO sole approval. Infrastructure provisioning; no business logic; closes a flagged deploy gate.
2026-05-13 (root-level docs path cleanup from 2026-05-13 reorganization)
- docs(shared): fix remaining stale path references from 2026-05-13 reorganization in root-level docs (AGENTS.md, PW-CHAT-CAPABILITIES, PW-ARCHITECTURE, PW-STATE). Move-only PR; no content changes. Historical CHANGELOG entries intentionally preserved as-is.
- Path corrections applied (30 lines across 13 files, perfect insert/delete symmetry confirms zero content drift):
shared/api/<vendor>.md→shared/architecture/api/<vendor>.md(3 sites: AGENTS.md ×2, PW-STATE-2026-05-11.md, runbooks/postmark-inbound-branded-subdomain.md)shared/PW-CHAT-TOOLS-AUDIT-2026-04-23.md→shared/architecture/chat-tools-audit/PW-CHAT-TOOLS-AUDIT-2026-04-23.md(7 sites: PW-CHAT-CAPABILITIES, PW-ARCHITECTURE, PW-STATE, architecture/BLUEPRINT, architecture/PW-CACHING-LAYER, strategy/pii-manifest-schema, strategy/ONBOARDING, plus DELTAS-2026-05-02 mirror)shared/PW-CHAT-TOOLS-AUDIT-DELTAS-2026-05-02.md→shared/architecture/chat-tools-audit/PW-CHAT-TOOLS-AUDIT-DELTAS-2026-05-02.md(3 sites: PW-STATE, architecture/BLUEPRINT, compliance/reg-s-p/reg-s-p-compliance-record-2026)shared/strategy/BLUEPRINT.md→shared/architecture/BLUEPRINT.md(5 sites in strategy/ONBOARDING.md)shared/incident-response-plan.md→shared/compliance/posture/incident-response-plan.md(1 site in strategy/ONBOARDING.md)shared/PW-SUBPROCESSORS-v1_1.md→shared/compliance/posture/pw-subprocessors-v1_1.md(3 sites: PW-STATE, strategy/ONBOARDING, compliance/wisp-compliance-hub-plan)shared/PW-SUBPROCESSORS-*.mdwildcard → resolved to canonicalshared/compliance/posture/pw-subprocessors-v1_1.md(1 site in compliance/vendor-docs/README)shared/PW-SECURITY-POSTURE-PARTNER-v1_1.md→shared/compliance/posture/pw-security-posture-partner-v1_1.md(2 sites: PW-ARCHITECTURE, PW-STATE, architecture/PW-CACHING-LAYER)shared/PW-LEGACY-SERVICES-MIGRATION-PLAN-REVISED-2026-04-24.md→shared/status/PW-LEGACY-...md(1 site in PW-ARCHITECTURE)shared/strategy/emf-canonical.md→shared/emf-canonical.md(1 site in PW-ARCHITECTURE — emf-canonical is at root, not in strategy/)shared/strategy/blockskunk-phase-0-input-package.md→shared/compliance/audits/blockskunk-phase-0-input-package.md(1 site in compliance/reg-s-p/reg-s-p-compliance-record-2026)
- Verification: post-edit scan shows zero stale references remain in non-archive, non-CHANGELOG, non-inbox, non-status (date-snapshot) files. Remaining hits live in
archive/**(frozen),inbox/**(session captures),status/**(date-stamped revisions of state docs),compliance/changelog.md(historical log),compliance/decisions/PW-ONCHAIN-...(point-in-time decision record),compliance/cco-approvals/**(point-in-time approval records),strategy/CURRENT-STATE.md(date-stamped "As of" entries), andstrategy/pwos-advisor-agent-discovery-pass-2.md(2026-05-09 discovery snapshot) — all intentionally preserved as historical artifacts. - CTO/CISO sole approval. Pure path cleanup; no content drift. Closes the dangling-reference follow-up flagged in CHANGELOG entry "compliance docs consolidation + archive + dangling-ref cleanup" (PR #65).
2026-05-13 (pw-os-v2 chat export to PDF + format selector)
- feat(pw-os-v2):
POST /api/advisor/chat-export/pdflands. Sibling endpoint to the Google Docs path that shipped earlier today; same canonical serializer (lib/chat-export/serialize-to-markdown.ts), samechat.export.createdaudit verb. Two delivery modes: (a)application/pdfbytes returned withContent-Disposition: attachment; filename="PW-Chat-YYYY-MM-DD-…pdf"for browser download (default); (b) Postmark transactional email with the PDF as a base64 attachment whensend_to_email: true+email_address. - PDF generation via pdfkit (already a runtime dep; sibling to
apps/api/src/lib/pdf-generator.ts). New moduleapps/api/src/lib/chat-export/generate-pdf-from-markdown.tsparses the serializer's markdown back into structured turns + renders via PDFKit primitives. No pandoc / xelatex / texlive on the Cloud Run image — pdfkit is in-process. Footer per chat-export spec: advisor name + email + page numbers (NOT the CRD #335298 footer reserved for compliance documents). Disclaimer at the bottom: "This is a chat transcript export, not a personalized recommendation. See PW disclaimers at protocolwealthllc.com/disclosures." - Audit row shape (new
resource_type='pdf_export'):actor_id= advisor.sub;resource_id= opaquepdf_<uuid>;afterStatecarriestarget_kind='pdf',conversation_id,file_name,length_chars,message_count,tool_call_count,delivered_via('download' | 'postmark'),email_address(when delivered via Postmark),size_bytes,advisor_email. The Google Docs path keepsresource_type='drive_file'keyed by file_id. SEC examiners pivot onresource_id(audit-log search atpwos.app/admin/compliance/audit-log) to reconstruct any single export event end-to-end. - Frontend: the existing
ChatExportPreviewmodal grew a format selector (PDF | Google Doc; PDF default per CCO-prioritized advisor preference). PDF branch adds an "Email PDF instead of download" checkbox + email input. Google Doc branch keeps the existing "PWOS Advisor Drafts" target. Download path usesBlob+URL.createObjectURL+ synthetic anchor click (Safari-safe URL revoke on next tick). Success-state banners differentiate download / email / drive. - Audit doc delta:
shared/architecture/chat-tools-audit/PW-CHAT-TOOLS-AUDIT-DELTAS-2026-05-13.mddocuments the Alternative Pattern rationale (dedicated POST endpoint, NOT a chat tool with a two-turn confirmation gate — the modal click is the consent gate, advisor is the direct actor, Claude isn't in the loop). 14-layer defense-in-depth mapping included; no new anti-list entries triggered. - Test coverage: new
apps/api/src/lib/chat-export/generate-pdf-from-markdown.test.ts(14 tests: markdown parser + filename helper + PDF buffer shape with valid%PDFmagic + 10-turn pagination + empty-turn handling); 8 new tests inapps/api/src/routes/chat-export.test.ts(download branch, email branch with Postmark attachment shape, audit row contents, PDF-generation failure → 500, Postmark failure → 502, validation rejections). Full suite 1059 → 1081 passing; lint clean. - CTO/CISO sole approval. No compliance posture change (audit-log + 7-year retention lock already cover chat-derived deliverables); same PII redaction posture as the chat surface (placeholders preserved verbatim — raw NPI never enters the export pipeline because it never enters the chat in the first place).
2026-05-13 (pw-os-v2 chat cache_control 4-marker fix + rolling assistant-end marker)
- fix(pw-os-v2): chat 400 on multi-turn conversations with attachments. Anthropic caps
cache_controlbreakpoints at 4 per request.lib/attachments.tswas emitting one marker per PDF/image block; system + last-tool consume 2 more; a 4-turn conversation with attachments hit 6 markers and returnedinvalid_request_error: A maximum of 4 blocks with cache_control may be provided. Found 6.Cache is a longest-prefix match, so per-block markers were wasted budget anyway — only the rightmost marker per "section" affects the cache hit point.buildContentBlocksnow emits a single marker on the last content block whenenableCaching: true. - feat(pw-os-v2): rolling assistant-end cache marker. Replaces the prior "most recent historical user-with-attachments" marker (the bug-fix shape) with a marker at the end of the most recent assistant message in history. The assistant message is converted from
content: string→content: [{type:'text', text, cache_control:{type:'ephemeral'}}]; Anthropic SDK accepts both forms. Caches a strictly longer prefix (includes the assistant reply, often the largest single block) and works regardless of whether earlier user turns had attachments. Final 4-marker budget: 1 system + 1 last-tool + 1 last-assistant-end + 1 current-user, independent of turn count. - Test coverage: new
apps/api/src/lib/attachments.test.ts(5 tests) pins single-marker placement, the no-cache path, the no-attachment path, and the regression shape (4 PDFs + caching = 1 marker, not 4). Full suite 1033 → 1059 passing. - Observability follow-up: 24–48h Langfuse trace observation window before deciding on the 1-hour ephemeral TTL (
cache_control: {type:'ephemeral', ttl:'1h'}beta). The 25% cache-write premium needs to be justified by actual session-gap profile fromcache_read_tokensdistribution shifts toward the assistant turns. - CTO/CISO sole approval. Cache optimization on the chat surface; no compliance posture change; no client-data impact (chat was 400-ing pre-fix, no traffic ever reached Anthropic with the bad shape).
2026-05-13 (WISP AI posture consolidation + final navigation cleanup)
- chore(shared): consolidate
wisp-ai-postureinto one canonical document and tighten navigation across shared/. The WISP AI posture was previously split acrosscompliance/wisp-ai-posture-2026-04-30.md(v1.0, 194 lines of content) andcompliance/wisp-ai-posture-2026-05-12.md(v1.1, a 60-line delta that referenced v1.0 for unchanged sections). This pass merges them into a singlecompliance/wisp-ai-posture.md(v1.2, self-contained, ~190 lines) with all v1.0 content plus v1.1's §1 bullet 6 ingress-posture correction and Marketing Rule §206(4)-1 row. Cross-document path references updated to post-2026-05-13-reorg locations; §248.30(a)(5) citation aligned to the SEC final-rule structure; §8 roadmap updated to reflect Compliance Hub Phase B + C as shipped 2026-05-08. v1.0 + v1.1 retired toarchive/compliance/wisp-ai-posture-history/with a per-bucket README documenting the supersession. - External cross-references updated to point to the new canonical:
PWOS.md(§8.6 + document references),PW-STATE-2026-05-11.md(§AI governance + companion-docs list),architecture/BLUEPRINT.md(§4 HITL Tier 2 anchor + §5 AI surface footer reference),compliance/README.md(canonical-documents table row). The two ref-sites incompliance/decisions/PW-ONCHAIN-REPOSITIONING-DECISION-2026-05-12.mdthat mention the dated v1.1 filename remain unchanged because they are describing what happened on 2026-05-12 (historical record). - Root
shared/CLAUDE.md+shared/README.mdupdates to fix the prior path inaccuracy: KYC/AML and WISP AI Posture live atcompliance/root, notcompliance/docs/.compliance/docs/is now explicitly documented as reserved for finalized program documents migrated from/mnt/project/(Compliance Manual, Code of Ethics, ADV Part 3, etc.), with a placeholder README inside the directory listing the queued migrations and their CCO-led tracking surface. compliance/changelog.mdupdated with the four cleanup waves shipped today (PR#62reorg,#63pipeline + first PDFs,#64cross-doc reconciliation + KYC/AML v1.0 + Reg S-P Record 2026,#65docs consolidation + archive + dangling-ref cleanup, and this WISP consolidation).- Net effect: every PW compliance document now exists in exactly one canonical location with the version, owner, and signed-PDF status visible at
compliance/README.md. The rootshared/CLAUDE.mdandshared/README.mdpoint readers to that index as the entry point for any compliance navigation question. Stale references to pre-reorg paths inside compliance/ are zero (verified via grep); the remaining instances live inarchive/and in CHANGELOG history entries, where they correctly describe a moment in time.
2026-05-13 (compliance docs consolidation + archive + dangling-ref cleanup)
- chore(shared): consolidate compliance documentation; archive pre-pipeline PDFs; fix dangling references from the 2026-05-13 reorganization. Replaces the stale
compliance/README.md(pointed to several pre-reorg paths that no longer exist) with a comprehensive document index listing the canonical version of every PW compliance document, its source markdown path, version, owner, and signed-PDF location. Adds a siblingcompliance/reg-s-p/signed-pdfs/README.mddocumenting the version trail, DocuSign envelope workflow, and the recommended signing order for the three Reg S-P + AML artifacts in CCO queue. Addsarchive/compliance/pre-pipeline-pdfs-2026-05-13/README.mddocumenting the four PDFs moved into archive: three user-hand-rendered pre-pipeline PDFs (compliance/kyc-aml-policy-v1_0.pdf,compliance/reg-s-p/customer-breach-notification-template.pdf,compliance/reg-s-p/reg-s-p-compliance-record-2026.pdf) and the superseded first-generationcustomer-breach-notification-template-v0_1.pdf. Each is preserved under archive with a-pre-pipelinesuffix or its original versioned name for 7-year retention per Rule 17a-4 conservative posture. - Dangling-reference fixes within
compliance/: five files updated.compliance/governance-dashboard.md: four references toshared/incident-response-plan.md→shared/compliance/posture/incident-response-plan.md.compliance/ciso-dashboard.md: two references toshared/incident-response-plan.md→shared/compliance/posture/incident-response-plan.md; "Related references" block updated to point to the actual current paths (posture/incident-response-plan.md,governance-dashboard.md,architecture/api/turnkey.mdsince the api/ subtree moved during the 2026-05-13 reorg) and to drop the non-existentphase-3-followups.mdpointer (consolidated into the dashboards themselves).compliance/wisp-ai-posture-2026-05-12.md: one reference toincident-response-plan.md→compliance/posture/incident-response-plan.md.compliance/posture/incident-response-plan.md: §10 (open gaps) and §10A.4 (planned automation) updated to point to the livegovernance-dashboard.mdandciso-dashboard.md"Open items" sections instead of the non-existentphase-3-followups.mdtracker. IRP PDF regenerated through the pipeline (incident-response-plan-v1_2.pdf) to pick up the fix.
- Dangling references outside
compliance/are noted as a follow-up. Several root-level shared docs (PWOS.md,PW-CHAT-CAPABILITIES-AND-ROADMAP-2026-05-01.md,PW-ARCHITECTURE-DEEP-DIVE-2026-04-24.md,AGENTS.md,PW-STATE-2026-05-11.md,CHANGELOG.mdhistorical entries) still reference pre-reorg paths (shared/PW-CHAT-TOOLS-AUDIT-2026-04-23.md,shared/api/<vendor>.md,shared/strategy/BLUEPRINT.md,shared/incident-response-plan.md). Scope-out from this cleanup since those files are large authoritative docs maintained separately; they should be reconciled in a focused follow-up PR. - Net effect for navigation: opening
compliance/README.mdnow gives a single-screen table of every canonical PW compliance document with its source markdown, version, owner, and signed PDF. No more clicking through stale relative links from the pre-reorg layout.
2026-05-13 (compliance docs cross-reconciliation + KYC/AML v1.0 + Reg S-P Compliance Record 2026)
- feat(shared): cross-document reconciliation pass on the three Reg S-P + AML artifacts in CCO signing queue. All three documents (Customer Breach Notification Template, Reg S-P Compliance Record 2026, KYC/AML Policy v1.0) now share consistent: (a) IRP version reference (v1.0, matching the actual file); (b) sign-off block format (PW underscore-line pattern matching the KYC/AML §13 block, replacing the previous "Pending" table cells in the other two); (c) cross-references to companion documents; (d) §248.30 final-rule citation scheme.
- Customer Breach Notification Template bumped v0.2 → v0.3. Change log entry documents the IRP-reference tightening. §8.1 Sign-off section converted from a table to an underscore-line signature block for DocuSign compatibility. End-of-document marker updated to v0.3.
- Reg S-P Compliance Record 2026 bumped 2026.0 → 2026.0.1. §2.2 IRP version reference and §4 documentation table both tightened from "v1.x" to "v1.0". §4 documentation table line for KYC/AML Policy updated to reference
compliance/kyc-aml-policy-v1_0.md(path waskyc-aml-policy.md) with the version "v1.0 under PW letterhead; pending CCO + CTO/CISO sign-off" replacing the prior "Pending CCO rebrand from prior Enclave letterhead" placeholder. §4 open items note for the KYC/AML rebrand updated to reflect that v1.0 is drafted. §8.1 Sign-off section converted from a table to an underscore-line signature block. - KYC/AML Policy v1.0 lands. First PW-headed version under SEC Reg S-P amended posture. Replaces the legacy Enclave Digital Wealth-headed version effective March 1, 2025 (signed CCO April 16, 2025); substantive AML framework preserved. Entity references updated; AML Compliance Person designation aligned with the Resolution of the Managers dated January 13, 2026 (DocuSign Envelope
9854DBBF-EB8C-47F1-8071-9D4063C65E47). §13 Approval block uses CCO's full title "Chief Compliance Officer / AML Compliance Person". - 3 PDFs in
compliance/reg-s-p/signed-pdfs/ready for CCO DocuSign workflow:customer-breach-notification-template-v0_3.pdf(24 pp)reg-s-p-compliance-record-2026-v2026_0_1.pdf(16 pp)kyc-aml-policy-v1_0.pdf(24 pp)
- Pipeline change:
scripts/compliance/pw-letterhead.texnow loadsamssymbso pandoc-emitted task-list checkboxes (- [ ]→\item[$\square$]) render correctly. Required for the Reg S-P Compliance Record's §9 internal-validation checklist. - Pipeline usage discipline: documents that carry a native sign-off / approval section in the markdown body render WITHOUT
--signature. The flag is reserved for documents that lack a native block. Avoids the duplicate-signature-block footgun where the template would append a generic "Chief Compliance Officer" block under a document whose native block already uses a stronger title (e.g., KYC/AML's "Chief Compliance Officer / AML Compliance Person"). - Recommended signing order for CCO, per the audit-recommended chain: (1) KYC/AML Policy v1.0 — closes the Enclave-letterhead gap that the Reg S-P Compliance Record §4 inventory references; (2) Customer Breach Notification Template v0.3; (3) Reg S-P Compliance Record 2026.0.1 — signed last so its §4 documentation table reflects all upstream signings.
2026-05-13 (compliance PDF generation pipeline)
- feat(shared): markdown → PDF pipeline for PW compliance documents. New
scripts/compliance/generate-compliance-pdf.shwraps pandoc + xelatex with a custom letterhead (scripts/compliance/pw-letterhead.tex). Output is DocuSign-ready: firm-name header, "SEC RIA • CRD #335298 •" footer, "Page X of Y" page numbers, auto table of contents for documents over ~1,750 words (≈ 5 pages), optional CCO + CTO/CISO acknowledgment block when --signatureis passed. Title is auto-extracted from the first H1 of the source; effective date reads from YAML front-matter or defaults to today (UTC) + "(draft)". Internal markdown links are folded to "(see path/file.md)" so PDFs do not carry broken hyperlinks; HTML comments are stripped. Greek + math glyphs (σ, μ, α, β, δ, π) get a DejaVu Serif fallback so the IRP's "5σ" anomaly threshold renders correctly. - First batch shipped to
compliance/reg-s-p/signed-pdfs/(3 PDFs, 285 KB total):customer-breach-notification-template-v0_1.pdf(24 pp) — Reg S-P §248.30(a)(4) customer-notification template for CCO review; signature block present.incident-response-plan-v1_2.pdf(16 pp) — IRP v1.2 with signature block.pw-subprocessors-v1_1.pdf(9 pp) — public-facing subprocessors list, classified "Public on protocolwealthllc.com/subprocessors", no signature (public-facing artifact).
- Pipeline documented at
runbooks/compliance-pdf-generation.md: install commands for WSL/Debian/Ubuntu + macOS, usage examples, naming convention (<basename>-v<MAJOR>_<MINOR>.pdffor drafts;-signed.pdfsuffix for the DocuSign returned PDF), DocuSign envelope workflow (CCO → CTO/CISO → optional counsel), styling guidance, and the 7-year Rule 17a-4 retention posture (drafts + signed PDFs land in the samesigned-pdfs/folder and are git-committed alongside the source markdown). - Operational tooling, no business logic. CTO/CISO approval only. One-time system-deps install (
pandoc texlive-xetex texlive-fonts-recommended texlive-latex-extra) is captured in the runbook; the pipeline preflights both binaries and emits a stable install hint when either is missing. scripts/compliance/is a new top-level directory inshared/. Justification: pipeline scripts depend on a co-located LaTeX template, so a single directory for the pair is clearer than splitting betweenbin/(for the shell wrapper) and an ad-hoc template location. Future compliance tooling (e.g., signed-PDF audit-trail scripts) extends the same directory.
2026-05-13 (Reg S-P §248.30(a)(4) customer-notification template)
- feat(shared): Customer Breach Notification Template v0.1 drafted for CCO review. New
compliance/reg-s-p/customer-breach-notification-template.md(641 lines) is the pre-approved Reg S-P §248.30(a)(4) customer-notification artifact, pre-positioned for the smaller-entity compliance date of June 3, 2026. Eight required sections: Purpose; When notification is required (with the §248.30(d)(11) sensitive-customer-info table mapped to PW's actual data surface, the "reasonably likely" trigger criteria, investigation flow, full decision tree for the "no substantial harm or inconvenience" exception, and the §248.30(a)(5)(ii) 72-hour service-provider clause); Pre-Approved Notification Template (verbatim letterhead + body + signature block, with the six §248.30(b)(3) mandatory elements as a pre-send checklist); Identification of Affected Customers (SQL query template againstaudit_log); Delivery Mechanism (Postmark / Gmail-DWD primary, USPS Certified secondary, service-provider on-behalf-of clause); Documentation per incident (7-year retention per Rule 17a-4 conservative interpretation); Post-notification review (30-day post-incident review, quarterly CCO review, IRP update path); Approval cadence (joint CCO + CTO/CISO sign-off, quarterly review, incident-triggered unscheduled review). Appendix A cross-references each section to the IRP, Subprocessors v1.1, and Compliance Manual. Appendix B is the CCO pre-send checklist. Voice: PW-style (no em-dashes, semicolons where pause is needed, fiduciary-clear). Ready for CCO redline and DocuSign signature workflow.
2026-05-13 (shared/ directory reorganization)
- docs(shared): reorganize
shared/intocompliance/{docs,posture,audits,gap-maps,reg-s-p,decisions,vendor-correspondence}/+architecture/+strategy/structure. Three new documents added:strategy/2026-05-13-investment-meeting-decisions.md(investment-meeting captures),compliance/audits/blockskunk-phase-0-input-package.md(BlockSkunk Phase 0 engagement input),compliance/gap-maps/2026-05-13-compliance-doc-gap-map.md(cross-validation compliance gap analysis with Reg S-P amended June 3, 2026 deadline focus). Move-only PR (no content changes); preserves git history viagit mv. Root-level files relocated:PW-CHAT-TOOLS-AUDIT-2026-04-23.md+-DELTAS-2026-05-02.md→architecture/chat-tools-audit/;error-response-pattern.md→architecture/;incident-response-plan.md→compliance/posture/;PW-SUBPROCESSORS-v1_1.md→compliance/posture/pw-subprocessors-v1_1.md(lowercased);PW-SECURITY-POSTURE-PARTNER-v1_1.md→compliance/posture/pw-security-posture-partner-v1_1.md(lowercased);api/*(28 files) →architecture/api/;strategy/BLUEPRINT.md→architecture/BLUEPRINT.md. NewREADME.md(human navigation) +CLAUDE.md(agent navigation + organization principles) atshared/root.
2026-05-13 (pw-os-v2 ScanError.provider enum sync)
- feat(pw-os-v2):
ScanError.providerenum extended to mirror today's pw-api fundamentals-chain providers. pw-api shipped 5 PRs today (#206Marketstack,#208EDGAR XBRL,#209ifrs-full taxonomy,#210ETF short-circuit) that addedmarketstack,edgar,twelvedata, andmboum_thin_etfas named provider attempt values in the/scan-bundleresponse shape. pw-os-v2'sScanError.providerwas still'mboum' | 'fmp' | 'both' | null— every error attributed to those new providers got null'd in the runner's error-display path. Type now reads'mboum' | 'marketstack' | 'edgar' | 'twelvedata' | 'fmp' | 'multi' | 'mboum_thin_etf' | null; renamed'both'→'multi'for accuracy now that ≥5 providers can be attempted. providerFromBundleCode()pattern-match extended to recognize the new error-code prefixes:marketstack_/ms_→marketstack;edgar_→edgar;twelvedata_/td_→twelvedata;mboum_thin_etfexact match →mboum_thin_etf(ETF short-circuit attribution); synthetic codes (both_providers_thin,both_providers_empty,mboum_thin_no_fallback) →multi.- Runner attribution semantic clarified: the runner now PREFERS the bundle's
errorCodeattribution over theprovidersAttemptedflags. WhenerrorCode='fmp_4xx_404'AND bothmboum+fmpwere 'miss', the ScanError getsprovider='fmp'(the failing provider) rather than the older'both'fallback.multiis reserved for the synthetic codes above. One existing test assertion updated to match the new semantic; behavior change is more accurate. - 9 new tests in
scan_runner.test.tspin every new provider prefix + the renamedmultivalue + the null-errorCode fallback path (1033 passing total, up from 1024). No migration required (TypeScript-only). 'both'→'multi'rename is internal to pw-os-v2. pw-api'serror_codes.both_providers_thinsynthetic code stays as-is; the runner's pattern-match handles the legacy spelling. Downstream consumers (dashboards, audit_log writes) that readScanError.providerare TypeScript-typed and the compiler caught the one site that referenced'both'directly.
2026-05-13 (pw-api ETF short-circuit ahead of EDGAR)
feat(pw-api): ETF classifier short-circuits EDGAR for fund/ETF tickers when MBOUM is thin. Closes the second of two coverage gaps surfaced by today's validation scan (correlation_id
2c9dfb94-96b1-44ca-ac31-b8e8506b6ecc): POWR (Invesco DWA Power Sector ETF) + SHLD (Direxion Daily Healthcare Bull 2X Shares, historical) firededgar_fundamentals.ticker_no_cikbecause ETFs aren't in SEC'scompany_tickers.json(operating-company registry); they're incompany_tickers_mf.json(funds registry) and even with CIK resolution their XBRL filings (N-1A / N-CSR) carry no us-gaap or ifrs-full fundamentals concepts. New modulesrc/providers/edgar_xbrl/etf_classifier.tsexposesisLikelyETF(ticker)+classifyTickerType(ticker, tickerMap?, fundsMap?)with a hardcoded allowlist (~200 entries — SPDRs, Vanguard, iShares, Invesco, ARK, Direxion / ProShares leveraged products, Schwab, commodity ETFs, including POWR + SHLD specifically) and a conservative pattern matcher (Direxion-style prefixes). False positives mitigated via per-ticker allowlist verification; false negatives flow through the normal chain. Wired ahead of EDGAR inscan-data.ts: when MBOUM is thin AND the classifier fires, EDGAR + Twelve Data + FMP all skip cleanly. Bundle surfacesdata_source='mboum_thin_etf'(new distinct enum value, separate from'unavailable') +providers_attempted.edgar='skipped_etf'(new union member) +etf_skip: boolean(per-bundle signal letting the runner aggregatescanMetadata.etf_skip_count). Structured logs:etf_classifier.classified(per-classification, etf/fund only — operating companies don't log to avoid noise) +scan_data.etf_skip_edgar(chain-level, withreason= allowlist / pattern / sec_funds_map / sec_operating_map source). EMF scoring treatsmboum_thin_etfas unscoreable — ETFs are categorically out of EMF's universe.Future extension flagged: lazy-load
company_tickers_mf.json(~500KB) as a tertiary signal once allowlist maintenance shows wear. v1 covers PW's universe; the funds-map path is reserved.Test coverage: 26 new tests across
test/unit/etf-classifier.spec.ts(allowlist hits, pattern matching, case insensitivity, foreign ADRs correctly classified as operating-company, unknown fallthrough, logEtfClassification noise discipline) andtest/unit/scan-data-etf-shortcircuit.spec.ts(MBOUM-thin + ETF short-circuit path; MBOUM-hit + ETF normal path; operating-company + MBOUM-thin → EDGAR runs normally; etf_skip per-bundle signal for runner aggregation). Two existing tests inscan-data-twelvedata.spec.tsre-tickered from SHLD/POWR → OKLO/CMPS (operating companies) so chain-logic pinning isn't entangled with classifier behavior. Full suite: 812 → 838 passing.Cross-repo references: pw-api CLAUDE.md + README.md note the ETF short-circuit ahead of the provider chain.
shared/api/edgar.mdunchanged (the short-circuit is upstream of EDGAR; no taxonomy or endpoint changes).
2026-05-13 (pw-api EDGAR XBRL ifrs-full taxonomy extension)
feat(pw-api): EDGAR XBRL concept mapping extended with
ifrs-fulltaxonomy support. Closes a coverage gap surfaced by today's validation scan (correlation_id2c9dfb94-96b1-44ca-ac31-b8e8506b6ecc): EDGAR returned 200 for CMPS (COMPASS Pathways plc, UK-domiciled biotech, CIK 0001816696) butconcept_mapping.tsextracted zero fields because every tag in its priority lists was us-gaap-only — CMPS reports underifrs-fullas a 20-F foreign private issuer.TAG_PRIORITYnow keyed by taxonomy:{ 'us-gaap': [...], 'ifrs-full': [...] }per ScanBundle field. Resolver walks us-gaap first (priority preserved); on whole-list miss falls through to ifrs-full. Mappings seeded with the closest IASB-IFRS-Taxonomy equivalents per field:Revenue/RevenueFromContractsWithCustomers(revenue),ProfitLoss/ComprehensiveIncomeLoss(net_income),Assets(total_assets),CurrentAssets(current_assets),CurrentLiabilities(current_liabilities),NoncurrentBorrowings/LongTermBorrowings(long_term_debt),Equity/EquityAttributableToOwnersOfParent(shareholders_equity),NumberOfSharesIssued/NumberOfSharesOutstanding(shares_outstanding),CashAndCashEquivalents/Cash(cash_and_equiv),CashFlowsFromUsedInOperatingActivities(operating_cash_flow),PurchaseOfPropertyPlantAndEquipment(capex). FCF stays derived (ocf - |capex|); total_debt component sum keyed on whichever taxonomy resolvedlong_term_debt(us-gaap:LongTermDebt*+DebtCurrent+ShortTermBorrowings+CommercialPaper; ifrs-full:NoncurrentBorrowings+CurrentBorrowings+LongTermBorrowings).New response field + log signals.
ScanBundleResponseenvelope addsedgar_taxonomy_used: 'us-gaap' | 'ifrs-full' | 'mixed' | nullpopulated fromConceptExtractionResult.taxonomy_used; null when EDGAR wasn't attempted, otherwise reflects the dominant taxonomy across resolved fields ('mixed'for rare hybrid filers). New structured logedgar_concept_mapping.taxonomy_usedfires per-ticker on EDGAR hit with{ticker, taxonomy_used, concepts_resolved_count, correlation_id}so ops can pivot on the us-gaap / ifrs-full distribution at a glance. Existingedgar_concept_mapping.misslog payload extended additively withtried_taxonomies: ['us-gaap', 'ifrs-full']+ map-shapedtried_tags: { 'us-gaap': string[]; 'ifrs-full': string[] }.tags_usedshape on the extractor result changed fromRecord<string, string | null>→Record<string, { taxonomy, tag } | null>to carry the winning taxonomy alongside the tag name.Tests: 12 new (
test/unit/edgar-concept-mapping-ifrs-full.spec.tscovering CMPS-shaped ifrs-full happy path, hybrid filers with both taxonomies present, ifrs-full tag-fallback chains, miss case with both taxonomies tried, total_debt component sums per taxonomy;test/unit/scan-data-cmps.spec.tscovering the end-to-end CMPS regression with mocked MBOUM-thin + EDGAR-ifrs-full-hit). Existingedgar-concept-mapping.spec.tsus-gaap pinning preserved (3 assertions adjusted to the newtags_usedmap shape).scan-data-edgar.spec.tsextended withedgar_taxonomy_usedassertions. Full suite: 812 → 824 passing.Cross-repo references:
shared/api/edgar.mdXBRL section extended to enumerate the three taxonomies (us-gaap + ifrs-full + dei) and the per-field IFRS analogues. pw-api CLAUDE.md + README.md note dual-taxonomy coverage.
2026-05-13 (pw-api EDGAR XBRL fundamentals fallback)
feat(pw-api): SEC EDGAR XBRL wired as canonical fundamentals fallback in
/scan-bundle. Extends the existing EDGAR client (src/lib/edgar.ts) withfetchCompanyFacts+fetchCompanyConcept+fetchFramescovering SEC's/api/xbrl/companyfacts/CIK*.json,/companyconcept/.../taxonomy/tag.json, and/frames/taxonomy/tag/unit/CY*.jsonendpoints. Free, ToS-clean, no credit budgets, no API key. New modulesrc/providers/edgar_xbrl/concept_mapping.tswalks a tag-priority list per ScanBundle field (post-ASC-606RevenueFromContractWithCustomerExcludingAssessedTax→ pre-606Revenues→ deprecatedSalesRevenueNet, etc.), filters tofp='FY'annual values, collapses 10-K/10-K/A duplicates by latest filed date, and returns current + prior periods with full ScanBundlePeriod coverage (revenue, net_income, gross_profit, assets, equity, OCF, capex, FCF=derived, total_debt=summed). Inserted into the scan-data.ts fallback chain as Step 4 between Marketstack (Step 3) and Twelve Data (Step 5). Gated byEDGAR_FUNDAMENTALS_ENABLEDenv var (default false; flip post-deploy viagcloud run services update). Process-singleton 8 req/sec rolling-window throttle applied to all four EDGAR fetch paths (20% headroom under SEC's 10/sec hard cap).data_sourceenum extends to include'edgar';providers_attempted+error_codesenvelopes gainedgarkeys (additive — older readers projecting{mboum, fmp}keep working); new response fieldedgar_concepts_resolved(number | null) lets the runner aggregatescanMetadata.apiCallCounts.edgar_concepts_resolvedover a scan. Structured logs:edgar_fundamentals.attempt/.result(parity withtwelvedata_fallback.*/marketstack_fallback.*shapes),edgar_fundamentals.disabled(flag-off path),edgar_fundamentals.ticker_no_cik(non-US listings, crypto, ADRs without SEC filings),edgar_concept_mapping.miss(per-field tag-priority exhaustion — surfaces unmapped concepts for future mapping refinement).Twelve Data downgraded to quaternary, flag-off in prod. Validated 2026-05-13 that Twelve Data free Basic tier does NOT include fundamentals endpoints — every
/income_statement,/balance_sheet,/cash_flowrequest returns 403 "available exclusively with pro or ultra or venture or enterprise plans" regardless of ticker.TWELVEDATA_FALLBACK_ENABLED=falseset on Cloud Run; code retained in case of a future Pro-tier upgrade. EDGAR XBRL replaces it as the canonical fundamentals path.New
/v1/internal/edgar/*endpoints. Three new routes:GET /edgar/company-facts?ticker=X(orcik=),GET /edgar/concept?ticker=X&taxonomy=us-gaap&tag=Y,GET /edgar/frames?taxonomy=us-gaap&tag=Y&unit=USD&period=CY2024Q4. All match the existing/edgar/submissionsZod-validated +{error, kind}envelope pattern. Endpoint catalog grew from ~38 to ~41.Cross-repo references: updated
shared/api/edgar.mdwith the XBRL surface section (endpoints + concept-mapping notes + rate-limit posture + drift-history entry); pw-api CLAUDE.md + README.md reflect the new providers + chain + endpoint count.
2026-05-13 (pw-os-v2 audit_log schema fix)
fix(pw-os-v2):
audit_log.resource_idwidened UUID → TEXT (migration 0053). Everyai.tool.calledinsert since the chat-tool audit path shipped has been silently failing on the SQL layer withinvalid input syntax for type uuid: "<tool_name>". Discovered 2026-05-13 via Cloud Logging audit —jsonPayload.action="tool.audit.write_failed"shows the failure across every registered tool (create_google_task,get_market_movers,drive_search,gmail_search,list_calendar_events,find_wealthbox_contacts,list_wealthbox_notes,list_wealthbox_tasks,get_fred_series,get_eia_dataset,list_covered_vaults,get_onchain_wallets,get_market_scan,list_google_task_lists,list_google_tasks). Root cause: migration 0015 typed the column UUID; tool-audit.ts writes snake_case tool names + Drive file IDs there.tool-audit.tscatches the error and logstool.audit.write_failedbut doesn't break the chat flow, so the schema gap was invisible. Pre-production discovery; no client data; no compliance disclosure required. Schema change widensaudit_log.resource_idUUID → TEXT (preserves indexidx_audit_log_resourcewith same partial-index predicate); same widening onaudit_anomaly_findings.resource_idfor type-compat with the audit_log rows it mirrors;write_audit_log()regenerated withp_resource_id TEXT(preserves migration 0048'sp_correlation_id/p_trace_idDEFAULT NULL parameters). Reconstructive audit path for the affected window:tool.audit.write_failedCloud Logging events (captures tool name + request_id + error),conversations+messagestables in pw-os Postgres,ai_audit_logparallel chat-turn record.pw-api parallel schema flagged for follow-up. pw-api's
audit_log.resource_idis also UUID-typed (migration 0001_foundation, reinforced in 0017 + 0027). All pw-api callers today pass domain UUIDs (profileRow.id,row.id,input.clientId, etc.) so no insert is failing today, but the column should be widened to TEXT in a sibling pw-api PR for cross-repo posture parity and future-safety. Not blocking; separate PR.fix(pw-api):
audit_log.resource_idwidened UUID → TEXT (migration 0038) for architectural parity with pw-os-v2#320. Closes the schema-parity gap flagged above on the same day. Two pw-api-specific divergences from pw-os-v2's 0053: (1)resource_idstaysNOT NULL(no pre-auth event path exists in pw-api today; all callers pass a value); (2)idx_audit_log_resourceis rebuilt WITHOUT theWHERE resource_id IS NOT NULLpartial-index predicate (no-op when the column is NOT NULL anyway — matches 0001_foundation's original index definition).write_audit_log()regenerated withp_resource_id TEXT; preserves the 10-positional-argtenant_id-first signature from 0027 (nocorrelation_id/trace_idparameters — those are pw-os-v2-only additions from 0047/0048). Drizzle schema mirror (src/db/schema/audit_log.ts) flipped totext('resource_id').notNull()./v1/internal/admin/audit-trailendpoint Zod schema relaxed fromz.string().uuid()toz.string().min(1).max(255)and SQL WHERE dropped the::uuidcast. Schema-drift integration test (test/integration/schema-drift.spec.ts) extended to assert TEXT column type +p_resource_id textin the function signature; new dedicated regression file (test/integration/audit-log-resource-id-text.spec.ts) pins the snake_case / Drive-file-id / UUID-string / NOT NULL contracts end-to-end. No active failures in pw-api at fix time — this is purely architectural-parity / future-safety work.
2026-05-12 (chore session)
fix: pw-os scaling pinned in terraform (
cloud-run-services.tf:237-238) — prevents future apply-drift of the 2026-05-12 min=1/max=2 policy. Closes the unexpected finding surfaced by Task 3'scloud-run-baselineapply, which silently reverted the 12:30 UTC gcloud hotfix back tomin=0/max=10on revisionpw-os-00270-8ss. Restored via gcloud (pw-os-00271-9xx) + terraform now matches policy (pw-infrastructure #178). Same drift pattern as memoryfeedback_terraform_env_var_drift, just applied to scaling rather than env vars.Pre-cron hardening for 2026-05-13 scheduled runs. Three concrete fixes + two verifications closed tonight rather than carrying into tomorrow's 09:00 UTC market scan + 13:00 UTC posture probe. pw-infrastructure
#175(PAT trigger fix oncloudflare-posture-probe.yml—secrets.PW_POSTURE_PROBE_PAT || secrets.GITHUB_TOKENfallback forgh pr create+gh pr mergeso bot PRs trigger required-check workflows). pw-infrastructure#176(terraformimport { ... }block adoptingpwos-market-scan-run-nextCloud Scheduler job into state — sibling ofpwos-market-scan-tier1-daily, matches deployed state exactly so plan is zero-drift after the first apply). Verification: pw-os-v2 + pw-apirequired_status_checks.contextsboth clean of anyAnalyze (*)entries; pw-infra cleaned by CTO/CISO after my earlier report flagged the self-block. Pre-flight: both scheduler jobs ENABLED, pw-os on revision00269-hlgwithworker_mode=scheduler_tick, no stranded queued jobs from today's run (Cloud Logging shows 1 queued + 1 succeeded for2026-05-12, both job_id3ae7534b-...).Pending operator action: create fine-grained PAT
PW_POSTURE_PROBE_PAT(scope:pull_requests:write+contents:writeonProtocol-Wealth/pw-infrastructureonly) and add as repo secret before tomorrow's 13:00 UTC scheduled run, otherwise the workflow falls back to GITHUB_TOKEN gracefully and the daily snapshot PR still opens but needs close+reopen to auto-merge. Full procedure in pw-infrastructure#175body.
2026-05-12 (pw-onchain absorption — Slice 1 + 1.5 + 2 + 3)
pw-onchain → pw-api absorption: first read endpoint live. Slice 1 (pw-api
#189→7f8837f, merged 17:05:47Z) flipped/v1/internal/onchain/walletsfrom a 503 migration stub to a real RLS-scoped handler reading thewalletstable inpw-core, with app-layerWHERE tenant_id = $Xbelt-and-suspenders on top of FORCE RLS (CI'sPOSTGRES_USER=pwapiis a superuser that bypasses FORCE RLS; production Cloud SQL IAM role is non-superuser). Also addedsrc/lib/trackers/base.ts(type-only port of pw-onchain's tracker shapes) andsrc/lib/retry.ts(withRetryhelper with tenacity-equivalent exponential backoff; throws on full exhaustion to preserve the silent-$0-snapshot regression-class fix). New canonical audit actiononchain.wallets.listed. Slice 1.5 (pw-os-v2#315→6d785505, merged 17:24:11Z) patched theget_onchain_walletschat tool to forwardtenant_id=SYSTEM_TENANT_IDinto the bridge call, narrowedOnchainWallet.wallet_idfromnumber→string, and updated theis_trackingdescription. Closes the single-window UX regression (400 tenant_id_required) created by Slice 1's tenant-scoping requirement. Wallets table is empty across all tenants today — endpoint returns{wallets: []}until a later slice seeds it from pw-onchain. Sidecar dep-fix PR#190forcedprotobufjs ^8.0.2via bothoverridesandpnpm.overridesto clear two same-day HIGH advisories pulled through@opentelemetry/[email protected]. Playbook updated atpw-onchain/docs/MIGRATION-TO-PW-API.md§9 (commitsfc79cf0+bbe5e10).Slice 2 — EVM tracker port + first real data delivery. pw-api
#195(51a846bd, merged 18:30:34Z) flipped/v1/internal/onchain/portfolio/summaryfrom 503 stub to a real handler wrapping a faithful TS port ofpw-onchain/backend/app/services/trackers/evm.py(~700 LOC). Advisor-chat'sget_onchain_portfoliotool now returns DeBank-sourced balances + DeFi positions instead of the migration notice. 3-source fallback chain (DeBank primary → Blockscout v2 → Etherscan/BaseScan native ETH); Alchemy skipped (not in pw-onchain source-of-truth either; deferred to slice 2.x if Blockscout coverage proves insufficient).Promise.allSettledtracker fan-out handles partial success per chain — one chain's RPC outage doesn't poison others. ETH spot pricing via freecoins.llama.fi/prices/current/(DefiLlama wraps CoinGecko, no new API key — extends existingsrc/lib/defillama.ts). New canonical audit actiononchain.portfolio.summary_requested. 16 new EVMTracker unit tests + 13 new integration tests (29 total). RLS posture: new integration test pins audit-row SELECT underpwapi_appnon-superuser role (CI-hardening PRs#193/#194landed during Slice 2 design window). Two CI-surfaced fixes during the PR:audit_log.resource_idis UUID-typed (switched fromwallet_addresshex →tenant_id); PII scanner redacts crypto addresses inafter_stateJSONB (audit-row tests query byrequest_idinstead — SEC-exam pivot path goes throughaudit_redaction_manifestsper Reg S-P §248.30(b)). Simplified spam filter (DeBankis_spam+ dust +is_core=false); fulltoken_blocklist.pyport (~629 LOC) deferred to a slice-X data-quality follow-up. Playbook §9 updated atpw-onchain/docs/MIGRATION-TO-PW-API.md(commit19370f9).Slice 3.5 — snapshot self-healing writer cron + nightly Cloud Scheduler job. pw-api
#200(companion: pw-infrastructure #179) ships the gap-fill writer for thedaily_snapshot_correctionsoverlay table that Slice 3 introduced.src/lib/snapshot-self-healing.tswalks recentdaily_snapshotsrows, refetches DeBankuser/total_balance(Source A) + DeBank-tokens-summed-via-DefiLlama-prices (Source B), and UPSERTs a correction row when sources diverge pastSELF_HEAL_DRIFT_TOLERANCE_PCT(default 5%) withcorrected_total_value = median(snap, A, B). New endpointPOST /v1/internal/cron/snapshot-self-healing/run-next(OIDC-only, cursor-resumable, bounded per-tick bySELF_HEAL_MAX_DURATION_MSracing Promise.race). Cloud Schedulerpw-api-snapshot-self-healing-nightlyfires0 4 * * *UTC, modeled onpw-api-cache-bust-summary(no app-layer second factor — Cloud Run IAM gate is the entire auth boundary on pw-api). New canonical audit actiononchain.snapshot_correction.written;resource_id = tenant_id(UUID-typed);wallet_addressrides inafter_stateJSONB redacted to<CRYPTO_ADDRESS_N>. 12 unit + 15 integration tests (27 new). Boundary: gap-fill writer ONLY — multi-source reconciliation against Octav, BTC UTXO walk reconstruction, LP NFT historical capture all stay in legacy pw-onchain per the 2026-05-12 boundary decision (compliance/decisions/PW-ONCHAIN-REPOSITIONING-DECISION-2026-05-12.md§3). No migration 0038 needed — Slice 3's 0037 hadnotes/source/drift_pctcolumns covering the writer's correction shape. Closes the absorption thesis at Slices 1-3 + 3.5.Slice 3 — daily_snapshot_corrections migration + portfolio/performance overlay reader. pw-api
#198(bc86e06, merged 19:10:34Z) flipped/v1/internal/onchain/portfolio/performancefrom 503 stub to a real handler wrapping a faithful TS port ofpw-onchain/backend/app/services/snapshot_aggregation_service.py. Migration0037_daily_snapshot_corrections.sqlports pw-onchain20260428_0043with three documented divergences (§B tenant_id added + FORCE RLS + tenant_isolation policy, §C CHECK-enforced lowercase 40-hex when entity_type='wallet', §D UPSERT semantics via UNIQUE (tenant_id, entity_type, entity_id, snapshot_date) — derived data, re-runnable). The "original observation" invariant lives at thedaily_snapshotslayer (immutable raw EOD); corrections are a regenerable overlay. Overlay reader (src/lib/snapshot-aggregation.ts, ~250 LOC) faithfully ports merge rules: correction's total_value wins; holdings/positions fall back to raw orcorrected_total_valuefor synthetic correction-only dates (pre-tracking history);is_corrected/is_syntheticflags surface in API. Endpoint shape:?tenant_id=&wallet_address=&start_date=&end_date=&chains=; chains param ACCEPTED but UNUSED (chains_supported: false— daily_snapshots is wallet-level, not per-chain). New canonical audit actiononchain.portfolio.performance_requested. 10 unit + 15 integration tests (25 new). RLS: route doesn't directly read tenant-scoped tables in handler, but overlay reader clausesWHERE tenant_id = $Xbelt-and-suspenders + new integration test pins cross-tenant isolation underpwapi_appnon-superuser role. Single CI-surfaced fix:daily_snapshots.holdings_count/positions_countare INTEGER NOT NULL — integration seed helper updated to supply 0 defaults. Writer side (snapshot_self_healing) deferred to Slice 3.5 — corrections table is empty in production until the writer lands; route returns base data withcorrections_applied: 0. Admin endpoint for back-office corrections has no port (pw-onchain doesn't have one either). Playbook §9 updated atpw-onchain/docs/MIGRATION-TO-PW-API.md(commit0edd36f).
2026-05-12 (CI hardening)
Theme: GHAS CodeQL default-setup disabled on the three active runtime repos after a systemic stuck-aggregator failure mode blocked three PRs in a single day. CTO/CISO operational decision documented per Rule 206(4)-7.
Failure mode observed (2026-05-12). GitHub Advanced Security's CodeQL aggregator check_runs (the meta "CodeQL" status that combines individual Analyze job results into a single required check) sat indefinitely in
queuedstate on three PRs across the active estate — pw-api#188(>30 min before admin-merge), pw-infrastructure#173(>27 min before admin-merge), and was about to block the Cloudflare-posture-probe bot PR (#174) flow on its first validation run. pw-os-v2#314escaped only by luck of timing (the aggregator happened to resolve in ~3s on that head SHA — same fast path that PR 172 took yesterday). Underlying Analyze (actions) / Analyze (javascript-typescript) jobs ALL succeeded on every PR — the failure is purely in the aggregator-to-check-run plumbing.Action taken. GHAS CodeQL default-setup disabled on pw-os-v2, pw-api, and pw-infrastructure. Required-status-check
CodeQLremoved from main branch protection on the same three repos. Custom-workflowAnalyze (actions)+Analyze (javascript-typescript)jobs (defined in each repo's.github/workflows/codeql.yml) continue running on every PR — they just aren't required for merge any more. For pw-infrastructure specifically,Analyze (actions)was promoted to the required-checks list (replacing the removedCodeQLaggregator) so the underlying CodeQL coverage is still gating merges; the difference is the required check now points to the actual workflow job that always reports, not the broken aggregator.Net effect.
- Same CodeQL static-analysis coverage on every PR.
- No more systemic GHAS aggregator failures blocking merges.
- Auto-merge actually fires on green CI (the original PW workflow design assumption).
- SARIF results still flow into the GitHub Security tab from the Analyze jobs'
upload-sarifstep; the Code Scanning UI continues to surface findings without the aggregator layer.
Authority. CTO/CISO operational decision (CTO/CISO). Documented in
shared/compliance/access-control-register.md2026-05-12 entry. SEC RIA cybersecurity program (Rule 206(4)-7) attestation: change reduces operational fragility without weakening the detection layer; the "Detect" function of NIST CSF for source-code SAST is preserved (Analyze jobs still run, SARIF still uploads, alerts still gate where needed for advisory rather than blocking).Tooling follow-up surfaced. Bot-authored PRs (e.g. the new
cloudflare-posture-probe.ymldaily snapshot PR pattern) created with the defaultGITHUB_TOKENdon't trigger downstreampull_requestworkflows — meaning theterraform-skip-non-terraform-prs.ymlcheck emitters don't fire. Auto-merge then sits forever waiting for required terraform-named checks. Decision pending on whether to add a fine-grained PAT secret to pw-infra for the snapshot workflow vs. switching the trigger model. Tracked under the same hardening pass.
2026-05-12 (late)
Theme: Sibling PRs after PR 8.4 — pw-api PR 186 (error_code disambiguation) + pw-os-v2 PR 8.5 (scheduler-driven worker tick + SIGTERM, retires the in-process poller).
PR 186 (pw-api
fix/pr186-scan-data-error-code-disambiguation).routes/scan-data.tsaddserror_codes: { mboum, fmp } | nullso callers can distinguish "MBOUM thin + FMP cascade not triggered" from "MBOUM thin + FMP 401" from "both providers thin." Synthetic top-levelerror_code='both_providers_thin'when both come back 2xx-but-thin; otherwise the top-level prefers the more-informative (non-thin) code. New HTTP-status-aware codes (mboum_4xx_404,fmp_5xx_500,fmp_4xx_429, ...) replace the priorkind-keyed names. Structured logscan_data.both_providers_thinfires only on the thin-thin branch. 7 new unit tests + full pw-api suite (645 tests) pass. Closes the ambiguity that today's 4 small-cap equity failures (CMPS, OKLO, POWR, SHLD) surfaced.PR 8.5 (pw-os-v2
feat/pr85-scheduler-driven-worker-tick). Retires the in-processmarket_scan_worker.tspoller introduced by PR 8.3. NewPOST /api/internal/scheduler/market-scan/run-nextclaims one queued job (FOR UPDATE SKIP LOCKED) + awaitsrunMarketScaninline + transitions on success/failure. Hard per-tick timeout via envRUN_NEXT_MAX_DURATION_MS(default 9 min; row marked failed witherror.code='tick_timeout'on overflow). Orphan-recovery sweep runs on every tick (10-min threshold, same as PR 8.3). SIGTERM/SIGINT handler added so Cloud Run scale-downs close the HTTP server cleanly. Boot log emitspw_os.bootwithworker_mode='scheduler_tick'so dashboards can detect a rollback.startMarketScanWorkerboot call removed fromapps/api/src/index.ts; the worker module stays in tree for a 48-hour soak window before deletion. New bootstrap scriptapps/api/scripts/setup-run-next-scheduler.shcreates the Cloud Schedulerpwos-market-scan-run-nextjob (*/2 9-10 * * *UTC; OIDC + X-Scheduler-Token; retry=2/min=30s/max=120s); follow-up pw-infrastructure PR adopts it viaterraform import. 7 new route tests + full pw-os-v2 suite (966 tests) pass.Architectural note: min-instances=1 stays. The 2026-05-12 hotfix set pw-os min/max=1/2; PR 8.5 does not roll min-instances back to 0. The architectural correctness here is "scheduler-driven worker tick is the right pattern for Cloud Run regardless of min-instances setting"; min=1 stays in effect for warm-container latency + headroom for upcoming client portal traffic + future background work.
Follow-ups queued. (a) Delete
lib/scanning/market_scan_worker.tsafter 48h soak; (b) move Cloud Scheduler job intopw-infrastructure/terraform/cloud-run-baseline/scheduler-jobs.tf(sibling ofpwos_market_scan_tier1_daily); (c) PR 7.7 BLUEPRINT.md codification of the three engineering rules surfaced by PR 8 series (async pattern, force-bypass idempotency, empirical pre-verification — per memoryproject_pr77_blueprint_engineering_rules).
2026-05-12
Theme: PR 8.3 09:00 UTC cron miss → root cause → hotfix → PR 8.4 (ETF routing + FMP observability). Ops hardening + access-control housekeeping.
PR 8.3 09:00 UTC cron failure — root cause. Cloud Run scaled pw-os to zero overnight after 23:09 UTC
lifetime_cap_reachedon 05-11; in-process worker died with the instance. Scheduler POST returned 202 in 126ms; instance idled out before the worker's 30s poll could claim job. Async pattern shipped in PR 8.3 is sound; min-instances=0 was the regression vector.Hotfix 12:30 UTC.
gcloud run services update pw-os --min-instances=1 --max-instances=2 --region=us-central1 --project=pwllc-prod. Worker booted on revision 00266; claimed job3ae7534b-edc2-485d-8267-70756fd577c7at 12:35:54 UTC; scan succeeded 12:39:45 UTC; wrotegs://pwllc-pwos-attachments/market-scans/2026-05-12.json. Revisionpw-os-00267-hf4current after max-instances update.Diagnostic finding. Scan scored 71/118 with 47
fundamentals_missing_both_providerserrors. Partition: 43 ETFs (correctly taggedassetType='etf') + 4 small-cap equities (CMPS, OKLO, POWR, SHLD,assetType='stock').scan_runner.tscalledfetchScanBundleon every row regardless ofassetTypedespitelayer_classifier.tsandregime_classifier.tsalready branching on it.PR 313 (PR 8.4) opened on pw-os-v2. ETF asset-class routing in
scan_runner.ts+ FMP fallback observability counter rename + structured logscan_runner.mboum_thin. Three commits:6244ac0type extension,2c11b54Fix 8.4-A,b3d1db1Fix 8.4-B + preflight script. 959 tests pass; lint + typecheck clean. Expected post-deploy:tickerCountScored71→114 (60% → 96.6%).Sibling ticket: pw-api PR 186.
routes/scan-data.ts:284error_code disambiguation (coalescesmboumErr ?? fmpErrso both-providers-thin always reads as MBOUM thin). Not coupled to PR 313 deploy.Ops hardening. Log-based metric
market_scan_succeededcreated viagcloud logging metrics create; GCS bucketpwllc-pwos-attachmentsset to retention=6y + versioning=on (Reg S-P §248.30(b) + Rule 204-2 books-and-records).Deferred to PR 8.5. In-process worker → scheduler-driven tick endpoint architecture rewrite; SIGTERM/graceful shutdown handler. Bug
#1(worker-singleton) becomes moot under the new architecture.Hygiene.
pw-advisorsarchived.pw-kincareDependabot Next.js 16.2.6 PR#2merged (clears 7-8 of 9 highs).pw-kincarearchive deferred until migration into pw-portal-v2 + pw-os-v2 completes.CTO/CISO access grant.
[email protected]granted membership inpw-{os,api,api-webhooks,nexus,onchain}-serviceIAM Postgres roles for diagnostic work. Documented in access-control register; inherited from existing service accounts, no privilege escalation.
2026-05-11
Theme: Option C foundation sequence + PR 8 family (Tier 1 daily market scan). PRs 4 / 5 / 7.5a / 7.5b set the foundation; PR 8 ships the scan code + chat tool; PR 8.2 closes three bugs surfaced by the 2026-05-11 manual trigger + activates the Cloud Scheduler + wires Hurst price history. PR 8.1 (scheduler-only) was rolled into PR 8.2 since the bug-fix work and the activation share the same verification pass. PR 8.2 hotfix (evening) adds force_refresh bypass on the idempotency lock. PR 8.3 (evening) converts the synchronous handler to async queue-and-acknowledge after the 21:10 UTC manual trigger hit the 60s Cloud Run ingress timeout at 59.998s. PR 8.4 (in-flight, scoped tomorrow morning from cron-tick evidence) addresses worker-singleton + MBOUM history + FMP fallback + sources provenance.
Shipped (full 2026-05-11 ledger)
- PR 8 series — Tier 1 daily market scan foundation (pw-os-v2
#308, pw-shared #34, pw-api #182). - PR 8.2 — 3 bug fixes + Cloud Scheduler activation + Hurst historical-closes (4-repo: pw-os-v2
#309, pw-api#183, pw-infrastructure#172, pw-shared #35). - PR 8.2 hotfix —
force_refresh:truebody param to bypass same-day idempotency lock; supersedes prior same-day rows; newmarket.scan.forced_rerunaudit verb (pw-os-v2 #310). - PR 8.3 — async queue-and-acknowledge pattern; handler returns 202 in <1s; in-process worker (30s poll, 25-min lifetime,
FOR UPDATE SKIP LOCKEDclaim, orphan recovery forrunningrows > 10 min old); new GET/api/internal/scheduler/market-scan/:jobIdstatus endpoint (pw-os-v2 #311). Resolves 504 timeout cap. - Empirical provider diagnostics — read-only curl probes (MBOUM/FMP/EIA/FRED via Secret Manager) confirming root cause per bug (pw-shared #36).
- PR 8.4-prep — Bug
#2fix —pw-api/src/lib/mboum.tsMboumHistoryBar.date → timestamp; parser preferstimestamp_unix * 1000integer sort, falls back toDate.parse(timestamp). Six new unit tests cover canonical shape, adjclose preference, ISO fallback, drop-on-no-timestamp, empty body, ticker= param. 632 → 638 pw-api unit tests pass. Shipped to close the universal 68/68 Hurst miss pre-cron so tomorrow's 09:00 UTC tick is a control case for the remaining bugs. - PR 8.4-prep — Bug
#5fix —pw-os-v2/apps/api/src/lib/scanning/types.tsMacroSnapshot + RawMacroSnapshot both gain optionalsources?: Record<string, string>; pass-through inmacroSnapshotFromRaw; newdata_client.test.tsexercises both branches. 948 → 950 pw-os tests pass. Macro provenance now flows pw-api → GCS scan output. shared/strategy/tier1-layer-overrides.md— authoritative under CTO/CISO authority effective merge date (in PR 8 / pw-shared #34).
In-flight
- PR 8.4 (narrowed scope after PR 8.4-prep wave closed Bug
#2+ Bug#5tonight):- Bug
#1— worker singleton diagnostic. Confirm whether the in-process worker spawns on cron-triggered cold starts (control case at 09:00 UTC tomorrow). Ifgs://pwllc-pwos-attachments/market-scans/2026-05-12.jsonexists, bug is manual-trigger-specific. - Bug
#3— FMP fallback chain audit. FMP free-tier production key empirically rate-limited on every call (429); pw-api needs to map 429 →provider_throttleddistinct from generic miss; also investigate whether stale "miss" cache entries from PR 8 morning's FMP-first regime are still hot (mboum:financials:<symbol>TTL in cache-policy.ts). Architectural decision: paid FMP tier vs. cache-shape fix vs. both. - Bug
#4hypothesis empirically rejected — no code action. PR 8.4 release notes document the operator's expectation gap. - Bug
#2✅ closed pre-cron — Hurst should populate tomorrow. - Bug
#5✅ closed pre-cron —macroSnapshot.sourcesshould appear tomorrow.
- Bug
Empirical provider diagnostics — 2026-05-11 evening (read-only probes, no code changes)
Probes ran against production keys from Secret Manager (pwllc-mboum-api-key, pwllc-fmp-api-key, pwllc-fred-api-key, pwllc-eia-api-key). Findings authoritative for PR 8.4 scoping:
| Bug | Hypothesis | Empirical result | Root cause |
|---|---|---|---|
#1 worker singleton |
Not initializing in production | Deferred to tomorrow's cron-tick control case | TBD by 09:15 UTC |
#2 MBOUM history 68/68 miss |
Wrong endpoint or shape | MBOUM returns 5 bars HTTP 200 with {timestamp, open, high, low, close, volume} for NVDA/SMH/OKLO |
pw-api parser reads b.date but MBOUM emits timestamp — silent NaN drop |
#3 FMP fallback never fires |
Path bug or 429 swallow | FMP returns HTTP 429 "Limit Reach" on every call; MBOUM CAN fetch ABBV/AMD financials with all expected row labels | Two distinct issues: (a) misclassification of MBOUM "misses" (MBOUM has the data); (b) FMP is rate-limited |
#4 Macro WTI/CPI wrong |
Wrong series or scaling | EIA RWTC=$109.76 (2026-05-04), CPI YoY=3.286%, PCE YoY=3.496% — scan output matches upstream byte-for-byte | No bug; operator's "expected" values are stale |
#5 sources field missing |
Field never shipped | pw-api /v1/internal/macro/snapshot emits sources per type; pw-os MacroSnapshot interface lacks the field; deserialization elides |
Type-erasure boundary on pw-os side |
Merged PRs — PR 8.2 (bug fixes + scheduler activation + Hurst wiring)
Bugs caught by the 2026-05-11 PR 8 manual trigger (gs://pwllc-pwos-attachments/market-scans/2026-05-11.json):
| # | Bug | Evidence | Fix |
|---|---|---|---|
| 1 | Silent provider failures (Rule 12 violation) | 111/118 tickers scoringProvider:"unavailable" with empty errors[] array |
scan-bundle returns error_code + providers_attempted envelope; scan_runner pushes an errors[] entry for every unavailable ticker; also for prices_missing when Hurst input is short |
| 2 | Wrong provider order (FMP-first failed on ~94% of universe) | Only 7/118 scored — mostly US large-caps that FMP free tier covers | scan-bundle now tries MBOUM first (paid $49/mo, broad coverage) and falls back to FMP. New fetchMboumFinancials in lib/mboum.ts parses MBOUM v2 financials table format with ×1000 scaling per pw-nexus reference |
| 3 | Macro unit + series errors | tgaBalanceBillions: 877761, wtiCrudeUsd: 109.76, brentCrudeUsd: 118.26 — TGA was in millions labeled as billions; FRED commodity series returning wrong values |
WTI/Brent/natgas switched to EIA spot series (RWTC / RBRTE / RNGWHHD via petroleum/pri/spt + natural-gas/pri/fut datasets); TGA switched to Treasury FiscalData v1/accounting/dts/operating_cash_balance (returns millions, divide by 1000 for billions). sources field added to macro/snapshot response so each metric carries its provenance |
Features in same PR set:
- Cloud Scheduler activation (
pw-infrastructureterraform/cloud-run-baseline/scheduler-jobs.tf) — newgoogle_cloud_scheduler_job.pwos_market_scan_tier1_dailyfires0 5 * * 1-5onAmerica/New_York(only PW scheduler job using ET; auto-handles EDT/EST).attempt_deadline=1800sfor the 118-ticker walk;retry_count=1since the agent_jobs idempotency check short-circuits same-day re-runs. Auth pattern matches the four pre-existing pw-os ticks (OIDC + X-Scheduler-Token per the 2026-04-23 newline-trim convention). - MBOUM historical-closes endpoint (
pw-api/v1/internal/historical-closes/:symbol) — returns up to 250 daily closes via MBOUM/v2/markets/stock/history. pw-osdata_client.fetchHistoricalClosesconsumes; scan_runner feeds the closes tocomputeHurst. Recent-IPO tickers with insufficient history surface asprices_missinginerrors[]per Rule 12.
Per-repo PR list:
- pw-api:
feat(scan-data): MBOUM-first + EIA macro fix + historical-closes(fetchMboumFinancials,fetchMboumHistoryadded tolib/mboum.ts; scan-bundle rewritten with provider tracking; macro/snapshot reroutes commodities + TGA; new/historical-closes/:symbolendpoint). 632 unit tests pass. - pw-os-v2:
fix(market-scan): populate errors[] + Hurst pull + provider tracking(scan_runner.tspushes Rule 12 entries;apiCallCountsextended withmboum_hits/mboum_misses/fmp_fallback_hits/fmp_fallback_misses/history_hits/history_misses;data_client.fetchHistoricalClosesadded). 933 tests pass. - pw-infrastructure:
feat(scheduler): pwos-market-scan-tier1-daily Cloud Scheduler job(scheduler-jobs.tfnew resource). - pw-shared: this CHANGELOG entry + CURRENT-STATE.md.
Post-merge verification protocol (per PR 8.2 brief):
- Manual trigger via
curl -X POST .../scheduler/market-scanwith X-Scheduler-Token. - Verify GCS object at
gs://pwllc-pwos-attachments/market-scans/2026-05-11.jsonhas:errors[]populated when failures occur (Rule 12 satisfied)apiCallCounts.mboum_hits > fmp_fallback_hitstickerCountScored ≥ 100/118(90%+ coverage target)macroSnapshot.wtiCrudeUsdin 50-70 rangemacroSnapshot.tgaBalanceBillions ≤ 1000
- If all clean → cron picks up next weekday 5 AM ET automatically.
- If issues remain → PR 8.3.
Earlier 2026-05-11 work — PR 8 (Tier 1 daily market scan foundation)
pw-os-v2:
- PR 8 —
feat(market-scan): Tier 1 daily scan foundation. Migration 0050 addsmaster_tickers(118-row universe withtier/scan_universe/layer_override+ EMF scoring snapshot columns) andticker_fundamentals(full financials cache; F-Score breakdown stored as JSONB; prior-period snapshot fields for delta-based checks). Migration 0051 seeds 118 Tier 1 tickers (74 stocks + 44 ETFs; one-off counting discrepancy vs brief's stated 117 — both included for completeness) with 71 deterministic layer overrides. Newlib/scanning/module: pure-logic scorers (piotroski_scorer.ts9-check F-Score,croic_calculator.ts8% threshold,hurst_calculator.tsR/S analysis), classifiers (layer_classifier.tsoverride precedence,regime_classifier.ts4-regime + 4-tier withALIGN_WITH_REGIME/NEUTRAL/FADE_REGIMEverdict), thindata_client.tsOIDC wrapper to pw-api (no vendor credentials in pw-os), andscan_runner.tsorchestrator that produces the per-day GCS JSON atgs://pwllc-pwos-attachments/market-scans/YYYY-MM-DD.json. NewPOST /api/internal/scheduler/market-scan(manual-trigger; X-Scheduler-Token +verifySchedulerToken+agent_jobsinsert/transition wired in; idempotent on today's ET date), newGET /api/advisor/scans/market/:date?(advisor-gated read), newget_market_scanchat tool (role: 'advisor', GCS-read with explicitmarket_scan_not_yet_generated/market_scan_too_old/market_scan_invalid_datereason codes).AgentJobTaskTypeunion extended with'market_scan'. Chat tool count: 56 → 57. Lambda decay + Perez phase + sector adjustments + ASAN + consistency + adversarial brief are explicitly deferred to PR 9+; the v1 tier ceiling isHIGH_CONFIDENCEwhen all four checks pass (F-Score ≥ 6, CROIC ≥ 8%, Hurst ≥ 0.55, regime-aligned ≥ 15% layer weight). Tests: 4 new unit suites (piotroski_scorer.test.ts,croic_calculator.test.ts,layer_classifier.test.ts,regime_classifier.test.ts) covering thresholds, override precedence, 4-regime classification, and tier assignment.
pw-api:
- PR 8 companion —
feat(scan-data): /v1/internal/scan-bundle + /v1/internal/macro/snapshot. New router atsrc/routes/scan-data.tsmounts under/v1/internal/and exposes (a) per-ticker scan-bundle (FMP profile + 2 annual periods of income/balance/cash-flow, parallel fan-out, fail-soft per upstream) and (b) macro snapshot (11 FRED series fan-out: VIXCLS, T10Y2Y, FEDFUNDS, UNRATE, CPIAUCSL, A191RL1Q225SBEA, PCEPI, DCOILWTICO, DCOILBRENTEU, DHHNGSP, WTREGEN). CPI + PCE YoY computed from 13 monthly observations; yield-curve spread converted from percentage points to basis points. Pre-existing fmp.ts / fred.ts wrappers do the actual upstream work — this PR adds the orchestration only.
pw-shared:
- New
shared/strategy/tier1-layer-overrides.md— authoritative under CTO/CISO authority as of merge date. Documents the 71 deterministic ticker-level layer assignments with per-ticker rationale and precedence rules. Quarterly review cadence.
PR 8 explicit deferrals (PR 8.1 + PR 9+)
- PR 8.1: Cloud Scheduler Terraform (
google_cloud_scheduler_job.market_scan_tier1_dailyfiring weekday 5 AM ET America/New_York) + scheduler-deployer IAM grants + MBOUM historical-close wrapper to populatecloses_daily(currently null → Hurst returns null v1 ceiling). Smoke-test within 1h post-Terraform-apply per the 2026-05-04 silent-401 protocol. - PR 9+: Lambda decay (schema field reserved as null), Perez cycle position, sector-specific adjustments (REIT AFFO, BDC NII, MLP DCF), ASAN structural-advantage screen, consistency CV scoring, adversarial brief generation (requires Claude API), base rate anchor, Tier 2 weekly scan.
Discovery-doc drift items closed
| # | Drift | PR |
|---|---|---|
| n/a | No daily firm-wide scoring pass over the Tier 1 universe; ad-hoc per-ticker chat queries only | PR 8 — scan_runner + GCS daily JSON + get_market_scan tool |
Earlier 2026-05-11 work — Option C foundation (PRs 1-7, 7.5a, 7.5b)
pw-os-v2:
- PR
#302—feat(agent-jobs): foundation + correlation_id propagation. Migration 0047 creates theagent_jobstable (14 columns + 4 indexes + status CHECK enumeratingqueued | running | succeeded | failed | cancelled) and addscorrelation_id UUIDcolumns to bothaudit_logandai_audit_logwith partial indexes. Newlib/agent-jobs/module exportsinsertJob,claimNextDueJob(FOR UPDATE SKIP LOCKED),transition(atomic UPDATE + audit_log row in one transaction, state-machine enforced at the application layer),getJob,listJobs,newCorrelationId. The audit-anomaly-scan handler is retrofitted to wrap its existing work in a job row (queued → running → succeeded/failed); the other three Cloud Scheduler ticks stay synchronous until the pattern bakes for a week. VerbAuditAction.AGENT_JOB_TRANSITIONED = 'agent.job.transitioned'added (TypeScript-only;audit_log.actionhas no DB CHECK constraint). 18 new tests across 7 classes (migration shape, state machine valid + invalid transitions, insert round-trip, lock semantics, retrofit success + failure, correlation_id round-trip). 841 tests pass. - PR
#303—feat(tools): get_client_brief — single-call per-client brief. Fans out via OIDC to existing pw-api/v1/internal/portfolio/client-view+/v1/internal/clients/listand returns a structured JSON brief covering client basic info + Quiltt accounts + onchain holdings + explicit{status:'unavailable', reason:...}for three sections that v1 cannot fill:altruist(no per-client internal endpoint exposed),recent_activity.wealthbox_notes+pending_items(contact_resolution_unavailable—wealthbox_contact_idis on the clients row but not in any internal endpoint response keyed by client_id;/portals/lookup-by-emailreturns it but is keyed by email — wrong direction),open_workstreams(workstream_items_no_client_fk— discovery doc Drift §23.4 confirmed at migration 0042). New assembler atapps/api/src/lib/clients/brief-assembler.ts(no inline assembler existed in pw-os-v2 to extract; the/api/advisor/clients/$clientId/portfolio/viewroute is a thin OIDC proxy). No route refactor, no new pw-api endpoint. Tool:role:'advisor',category:'portal', no confirmation gate (read-only). Output is JSON-stringified; flows through PR 2'svendor_tool_resultPII scan path (locked in by Fixture 6 —newScanState() + scan()against a synthetic brief containing an email substitutes the placeholder correctly). Chat tool count: 55 → 56. 22 new tests across 6 classes (registration, input validation, success, terminal failures, onchain unavailability variants, option flags, partial fan-out tolerance, PII scan readiness). 863 tests pass.
shared:
- Companion PR to
#302— schema docshared/strategy/agent-jobs-schema.md(10 sections, ~5,800 words). Schema doc revised after Step 0 to reflect the verb rename (agent_jobs.transition→agent.job.transitioned, caught by the registry's regex-shape test at audit-log.test.ts). - Companion PR to pw-os-v2
#303(get_client_brief). CHANGELOG entry only; no doc churn (the build surfaced a v1 limitation aroundwealthbox_contact_idexposure that's documented in the PR description rather than a new shared/strategy doc). - PR 6 of the Option C foundation sequence — state-doc sync + close
#205/#206. New authoritative state doc atshared/PW-STATE-2026-05-11.mdcapturing the post-PRs-1-through-5 snapshot. PreviousPW-STATE-2026-05-09.mdmoved toshared/archive/state/(archive/README.mdindex updated). On GitHub:Protocol-Wealth/pw-os-v2#205and#206closed with commenting comments citing merge SHAs —#205shipped across07fa9f0(Phase 1) +2cba472(Phase 2) +8ac3817(defense-in-depth fix);#206shipped acrossa757e56(rule engine + migration) +9bafea8(review surface) +2235acb(scheduler endpoint) +036fb64(PR 4 agent_jobs retrofit enhancement). Citedshared/compliance/wisp-ai-posture-2026-04-30.mdfrom the new state doc's §Compliance posture so it stops being orphaned per discovery doc Drift §23.4 flag. - PR 7 of the Option C foundation sequence — firm-wide engineering OS docs. Three new orchestration docs at
shared/strategy/:CURRENT-STATE.md(working memory; updated as last write of every PR; <500 words steady-state),BLUEPRINT.md(architectural map; 1,592 words; cited-not-duplicated),ONBOARDING.md(4 paths — employees/founders/advisors, contractor/intern, AI agent, compliance fast path — plus the PR ritual).wisp-ai-posture-2026-04-30.mdcited from BLUEPRINT.md §5 (AI surface) for its permanent home. Per-repo CLAUDE.md ritual updates land in companion PRs on pw-os-v2 / pw-api / pw-portal-v2 / pw-infrastructure / pw-website (each adds "update CURRENT-STATE.md as last write of every PR" to the post-merge deliverables list). Closes the Option C foundation sequence structurally; PR 8 advisor-agent-v1 is the next session. - PR 7.5a —
fix(agent-jobs): replace UPDATE-after-INSERT on audit_log with widened write_audit_log signature(pw-os-v2 #305). Closes the PR 4 dead-code-in-prod bug surfaced 2026-05-11 by manual scheduler-trigger diagnostic: theagent_jobsrunner'stransition()didUPDATE audit_log SET correlation_id = …AFTER the SECURITY DEFINER INSERT, tripping theaudit_log_no_updateBEFORE-UPDATE trigger (migration 0015) on every transition. Every audit-anomaly-scan tick from PR 4 merge (10:53 UTC) through 15:23 UTC failed silently with"audit_log rows are immutable; UPDATE not permitted"and returned HTTP 200 to Cloud Scheduler, defeating the retry policy. Migration 0048 widenedwrite_audit_log()withp_correlation_id UUID DEFAULT NULL(10th) +p_trace_id TEXT DEFAULT NULL(11th) — bundled to also close the latent telemetry-middleware trace_id gap from the original migration 0015 header (where trace_id was described as UPDATE-after-INSERT but no middleware ever existed; trace_id has been NULL on every audit_log row since the table shipped, and the planned middleware would have hit the same trigger). Handler now returns HTTP 500 onagent_jobs.setup_failedso Cloud Scheduler retries. CI gap closed: production triggers were covered only by live-DB integration tests (DATABASE_URL-gated); CI default lane ran without a DB, so PR 4 shipped past lint + unit-test. The mock pg fake inlib/agent-jobs/index.test.tsnow mirrorsaudit_log_no_update+audit_log_no_deletetriggers — any future UPDATE-on-audit_log defect fails at unit-test time. 867 tests pass; lint clean; typecheck clean. Post-deploy verification 2026-05-11 15:48:26 UTC: manualgcloud scheduler jobs run pwos-audit-anomaly-scan→ Cloud Scheduler logged HTTP 200, pw-os emitted INFOaudit_anomaly_scan.completewith job_id5e2254a0-…, correlation_ide3bba48e-…, duration_ms 517; zero ERROR logs in the trigger window. The retrofit is live in production for the first time since merge. - PR 7.5b (this PR) —
feat(session+cost): sliding session window + per-message cost visibility + Opus pricing correction(pw-os-v2 branchfeat/pr75b-sliding-session-and-cost-visibility). Two bundled concerns:- Sliding session window. Advisor was being kicked out mid-task by the 15-minute idle ceiling because the cookie didn't refresh on activity.
SessionClaimsgains aninitial_sign_in_atclaim preserved across re-issues;reissueSessionToken(claims)re-mints the cookie with a fresh 15-minute TTL while anchoring an absolute 4-hour cap from initial sign-in (Reg S-P §248.30(b) "reasonable" session length + stolen-cookie blast-radius reduction);loadSessioninrole-guards.tsre-issues on every authenticated request and treatsisAbsoluteMaxExceededas a soft 401 (caller sees unauthenticated → SessionIndicator renders the force-expiry modal). NewGET /api/session/statusendpoint (mounted inauth.tsbehindrequireSession) returns{expires_at, absolute_max_at, seconds_until_idle_logout, seconds_until_absolute_max, initial_sign_in_at}— single source of truth for the frontend countdown; the act of polling this endpoint itself slides the session, which is intentional (an advisor reading a long Claude response shouldn't time out under their nose). - Per-message cost visibility. Migration 0049 adds
cache_creation_input_tokens INTEGER+cache_read_input_tokens INTEGERto bothmessagesandai_audit_log(nullable; existing rows stay NULL — forward-only).claude-client.tsalready reads these counters off Anthropic'smessage_start.usageshape;appendMessage+writeAiAudit(both inconversations.ts) now persist them. SSEdoneevent fromroutes/chat.tscarriescache_creation_tokens/cache_read_tokens/cost_usd; GET messages computescost_usdserver-side per assistant row viaestimateCost. Frontend:Messagetype gainscost_usd?: number | null;MessageBubble.tsxreplaces the left-aligned{in} in · {out} outfooter with a right-aligned~$0.04cost (4-decimal precision under $0.10 so cents-of-cents moves on cached turns are visible); new<SessionIndicator>chip + cumulative$0.34 · 18 turnscount land in the chat header. - ⚠️ Opus pricing correction.
claude-cost.tshad Opus at $15/MTok input + $75/MTok output (the historical Claude 3 Opus rates). Anthropic's published rate for Opus 4.x is $5/$25 — 3× lower across the board. The correction is forward-only; no historical backfill. Every Opus cost figure in Langfuse generations,ai_audit_log.detail, and any internal spend report computed BEFORE 2026-05-11 is overstated by 3×. A drift-fence test (Opus pricing anchor — 2026-05-11 correctioninclaude-cost.test.ts) now anchors the rates so any future regression fails CI loudly instead of being baked into another month of spend data. Sonnet ($3/$15) and Haiku ($1/$5) rates were already correct. - Cache rates.
CACHE_READ_RATE_MULTIPLIER = 0.1(cached reads bill at 10% of normal input rate; this is the prompt-caching discount) andCACHE_CREATION_RATE_MULTIPLIER = 1.25(cache creation bills at 125%, a one-time premium). Without these, the~25K-83K cache_read_tokens/turnwe observe post-PR#242would have inflated displayed cost by 5–10× on cached turns and made the per-message footer useless for sanity-checking that caching was working. - Tests: 894 pass (was 867 in PR 7.5a). 40 new tests: 15 in
claude-cost.test.tscovering base pricing, cache-rate multipliers, realistic-turn end-to-end, format helpers, and the Opus drift fence; 25 added tosession.test.tscoveringinitial_sign_in_at(fresh + re-issue paths),reissueSessionToken(expiresAt + absoluteMaxAt anchoring),isAbsoluteMaxExceeded(legacy cookie, fresh, expired, boundary). Lint clean; typecheck clean across bothapps/apiandapps/web. - CHANGELOG flag for future spend analysts: when reconciling Opus spend across the 2026-05-11 boundary, divide all pre-correction figures by 3 to convert to the actual billed amount.
- Sliding session window. Advisor was being kicked out mid-task by the 15-minute idle ceiling because the cookie didn't refresh on activity.
Discovery-doc drift items closed
| # | Drift | PR |
|---|---|---|
| 9 | CLAUDE.md §Database listed agent_jobs + scheduled_jobs tables that no migration created |
pw-os-v2 #302 |
| Finding 3 | Scheduler endpoints did all work synchronously with no run-id / idempotency / first-class examiner-pivotable record | pw-os-v2 #302 (audit-anomaly-scan retrofit; other 3 ticks pending post-bake PR) |
| Finding 1 | Chat surface had no aggregator that fanned Wealthbox + Quiltt + Altruist + onchain into one tool result for an advisor agent | pw-os-v2 #303 (with three documented v1 limitations: altruist per-client endpoint not exposed, wealthbox contact_resolution_unavailable, workstream_items_no_client_fk) |
| 12 | Issues #205 + #206 listed open in PW-STATE-2026-05-09 but the underlying work shipped |
pw-shared PR 6 — issues closed with merge-SHA citations; new authoritative PW-STATE-2026-05-11.md |
| n/a | No curated reading path for humans + AI agents picking up cold | pw-shared PR 7 (this PR) + per-repo CLAUDE.md ritual updates — CURRENT-STATE.md + BLUEPRINT.md + ONBOARDING.md orchestrate the existing 30+ docs |
Compliance posture
- SEC Rule 17a-4 (books-and-records, 7y retention): every
agent_jobsstate transition writes one immutableaudit_logrow. Theagent_jobsrow itself mutates by design; the transition history is fully reconstructable fromaudit_logrows ofaction='agent.job.transitioned'ordered bycreated_at. The immutability trigger onaudit_logfrom migration 0015 stays enforced; the newcorrelation_idcolumn is ADD-COLUMN only. - Audit verb shape:
agent.job.transitionedmatches the registry's 3-segment dotted-lowercase-with-no-underscore-in-first-segment convention (auth.session.created,email.form.submitted,gsheet.rows_appendedpattern). Enforced byaudit-log.test.ts > AuditAction registry. - No DB CHECK constraint on
audit_log.action(verified atmigrations/0015_audit_log.sql:95—action TEXT NOT NULL), so the new verb is a TypeScript-only addition. PR 1's drift-fence test targetsai_audit_log.actiononly and is unaffected.
Sequence revision
The Option C closing PR shifted: PR 5 → PR 6 (state-doc sync, queued) → PR 7 (engineering-OS orchestration docs — CURRENT-STATE.md + BLUEPRINT.md + ONBOARDING.md) → PR 8 (advisor agent v1). PR 7 was originally the advisor-agent buildout; the engineering-OS docs that orchestrate the existing 30+ docs into curated reading paths now sit in front of the agent work to give it a stable orientation surface.
Known follow-ups for PR 8 (preconditions)
Deferred per PR 4 + PR 5 briefs; documented in pw-os-v2/CLAUDE.md + the relevant strategy / assembler docs:
- Stale-job sweeper for
agent_jobsrows that exceed Cloud Run timeout (60-minute hard cap). Non-blocking for the 5-second audit-anomaly-scan; may matter for PR 8's longer-runningmarket_scantask. correlationIdthroughToolExecutionContextso chat tools invoked under an agent_jobs context propagate the contract. Out of scope for PR 4 (audit-anomaly-scan doesn't call tools).wealthbox_contact_idexposure on/v1/internal/clients/listor/v1/internal/clients/:id— closes the v1contact_resolution_unavailablegap inget_client_brief, unlocks per-client Wealthbox notes + tasks. Probably a small pw-api PR landing alongside or before PR 8.- Altruist per-client read endpoint — closes the v1
altruist_per_client_endpoint_not_exposedgap if Altruist becomes part of PR 8's advisor agent surface. - Per-client linkage on
workstream_items— separate evaluation against the existing firm-internal workstream model.
Discovery-doc cross-reference
shared/strategy/pwos-advisor-agent-discovery-pass-2.md Drift #9, Finding 3, Finding 1, Drift §23.4 (workstream_items_no_client_fk).
shared/strategy/agent-jobs-schema.md (new in PR 4) §2 (schema), §3 (state machine), §4 (audit posture), §5 (correlation_id contract), §6 (worker pattern), §7 (retrofit), §10 (open questions + deferred items).
Sequence (revised): PRs 1 + 2 + 3 + 4 + 5 of 8 in the Option C foundation; PR 6 (state-doc sync + close #205/#206) is queued, then PR 7 (engineering-OS docs), then PR 8 (advisor agent v1).
2026-05-10
Theme: Option C foundation sequence — PR 1 (audit enum + workspace header), PR 2 (PII manifest cross-turn + within-turn), and PR 3 (Langfuse coverage on 4 non-chat surfaces) closing correctness + observability gaps surfaced by the 2026-05-09 advisor-agent discovery pass. Foundation work for the PWOS Advisor Agent v1; agent runner code is PR 7 in a later session.
Merged PRs
pw-os-v2:
- PR
#298—feat(audit): widen ai_audit_log.action enum + standardize workspace-id header. Migration 0046 admitsconversation.mode_changed(silently dropped on insert since 0038/session_mode landed;chat.ai_audit_write_failedevents with err=23514were the symptom). Two missingdefaultHeaders['X-PW-Workspace-Id']sites fixed (compliance/analyze.ts,workstreams/summarizer.ts);inbound-ai-classifier.tswas flagged unverified in the discovery doc and verified clean (routes throughclaude-client.getClient(), inherits the canonical header). Two regression-prevention drift fences added inapps/api/src/lib/. 811 tests pass; lint clean; typecheck clean. - PR
#299—feat(pii): merge tool-result manifest cross-turn + within-turn. Tool-result-discovered PII (fresh emails, crypto addresses, JWTs the advisor never typed) was detected by the in-processpiiScanStatebut never reached the per-conversation Redis manifest. Two bugs sharing one fix point inclaude-client.ts's tool-result handling block: cross-turn invariant break (turn N+1's<EMAIL_X>reached Anthropic as literal placeholder string) + within-turn leak (same-turn assistant response containing<EMAIL_X>streamed to the SSE writer with the literal placeholder visible to the advisor). Fix wiresmergeAndSaveManifestafter the existing tool-result scan, mutatesrehydrateManifest.placeholdersin place so the rehydrator's closure sees the new entries, drops the empty-placeholders short-circuit instream-rehydrator.tsso empty-but-non-null manifests select the buffer-and-rehydrate variant, and seeds an empty manifest whenreq.conversationIdis set. NewmanifestSyncQueueserializes parallel tool merges to prevent Redis SET race. Structured log:pii.manifest.tool_merge. 6 test fixtures (cross-turn, within-turn, collision, NAME/ORG/EVENT regression, TTL parity, parallel tools). 817 tests pass. - PR
#301—feat(observability): Langfuse coverage on 4 non-chat surfaces. Workstream summarizer, compliance analyzer, social drafts, and inbound classifier now emit Langfuse traces. Pattern documented in-tree atpw-os-v2/apps/api/src/lib/langfuse/PATTERN.md(drafted as Step 0 of the PR before any wiring).observability.tsextended with genericstartTrace/recordGeneration/finalizeTrace+classifyFailure(renamed fromclassifyChatFailure; old name kept as deprecated alias — removal tracked inProtocol-Wealth/pw-os-v2#300). CI guardlint-langfuse-fire-and-forget.mjsWRAPPER_ALLOWED_CALLERSexpanded 1 → 5 surfaces, with a comment pointing to the runbook section governing the set. Trace names:pwos.chat.message,pwos.workstream.summarize,pwos.compliance.analyze,pwos.social.draft,pwos.inbound.classify. 6 test fixtures (4 per-surface + 1 sensitive-data with compliance/analyze emphasis + 1 failure-mode regression). 823 tests pass. Sensitive-data discipline locked: vendor PDF text reaches Anthropic under the file-level skip-pii-scan carve-out but does NOT reach Langfuse trace metadata; the regression test serializes all captured trace args and asserts the PDF content is absent. Optional smoke-test script atpw-os-v2/apps/api/scripts/langfuse-smoke-test.ts.
shared:
- This PR — CHANGELOG entry; companion PR 2 schema doc
shared/strategy/pii-manifest-schema.md(drafted in PR-2 development cycle, included here). - Companion PR 3 runbook update at
shared/runbooks/langfuse-self-host-deploy-plan.mdadding a new §Coverage section that enumerates the 5 allowlisted Langfuse wrapper callers (chat + 4 new) and the governance rules for adding a sixth. Lockstep-required withWRAPPER_ALLOWED_CALLERSin the lint script.
Discovery-doc drift items closed
| # | Drift | PR |
|---|---|---|
| 4 | ai_audit_log.action CHECK diverged from runtime callers |
pw-os-v2 #298 |
| 10 | 2/4 direct new Anthropic() sites lacked X-PW-Workspace-Id header |
pw-os-v2 #298 |
| Finding 2 (cross-turn) | Tool-result PII never persisted to Redis manifest; turn N+1 lost cross-turn placeholder consistency | pw-os-v2 #299 |
| Finding 2 (within-turn — surfaced during PR 2 reconciliation) | Same-turn assistant response leaked literal <EMAIL_X> to SSE writer for fresh tool-result-discovered entities; rehydrator captured manifest pre-tool-loop with no growth path |
pw-os-v2 #299 |
| 11 | 4 non-chat Anthropic surfaces (workstream summarizer, compliance analyze, social drafts, inbound classifier) bypassed Langfuse trace coverage | pw-os-v2 #301 |
Compliance posture
- SEC Rule 17a-4 (books-and-records):
conversation.mode_changedinserts no longer dropped at the CHECK boundary. Existing rows untouched.ai_audit_loghas no immutability trigger by design (migration 0004 commentary "rows are append-only by application contract"); migration 0046 only DROP-and-readds the named CHECK constraint. - AI governance: forensic attribution restored on the four direct Anthropic SDK construction sites in
apps/api/src/lib/. ZDR enrolment is bound to the API key (workspace-scoped at the account level), so the missing header was an audit-affordance gap rather than a ZDR breach.
Discovery-doc cross-reference
shared/strategy/pwos-advisor-agent-discovery-pass-2.md §5 (audit_log + ai_audit_log), §16 (Anthropic API client configuration), Drift #4, Drift #10, Finding 2.
shared/strategy/pii-manifest-schema.md (new in PR 2) §6 (Write path — tool-result entities), §7 (Read path — verified verdict + reconciled trace), §7.1 (Within-turn rehydration fix point).
pw-os-v2/apps/api/src/lib/langfuse/PATTERN.md (new in PR 3) — canonical Langfuse wiring pattern; future Anthropic call sites must consult it before adding to the wrapper allowlist.
shared/runbooks/langfuse-self-host-deploy-plan.md §Coverage (new in PR 3) — source of truth for the wrapper allowlist; lockstep with WRAPPER_ALLOWED_CALLERS in the lint script.
Sequence: PRs 1 + 2 + 3 of 6 in the Option C foundation; PR 4 (agent_jobs foundation + correlation_id propagation) is queued.
Post-merge verification (recorded for the SEC books-and-records trail)
Cloud Logging query against the pw-os service:
resource.type="cloud_run_revision"
resource.labels.service_name="pw-os"
jsonPayload.action="chat.ai_audit_write_failed"
jsonPayload.err=~"23514"
Pre-merge (last 7 days) expected non-zero; post-merge (first 24h after revision rollout + migrate.applied for 0046_ai_audit_log_action_extension.sql) expected zero.
2026-05-09
Theme: 10-PR shipping day across 5 active repos. Production bug-fix wave (Wealthbox / FMP / FRED / onchain), full GCP+repo+vendor infra audit, ~$148/mo cost optimization landed (Cloud SQL HA drop), placeholder Cloud Run services retired, pw-portal-v2 CI gates brought to parity with pw-os-v2, vendor doc gaps closed (7 new + fmp.md refresh), shared/ archive cleanup (6 stale state snapshots + 4 closed planning docs moved to archive/).
Merged PRs
pw-api:
- PR
#177— fix(wealthbox): accept drifted GET response shapes (linked_toarray + meta-only envelope tolerance). - PR
#178— feat(onchain): stub/v1/internal/onchain/*withkind=onchain_migration_in_progress503; pw-onchain (Fly) decommissioned. - PR
#179— fix(api): FMP path drift (etf-holderrename +insider-tradingv3 base) + vendor key trim across all FMP/MBOUM/Tradier/FRED/etc keys (defends against paste-CR rotation artifacts). - Issue
#180filed: ETF sector/country weightings still 404 — needs live-key probe.
pw-os-v2:
- PR
#297— fix(chat): render clean migration message for onchain tools when pw-api returns the migration stub.
pw-portal-v2:
- PR
#47— chore(ci): wire ESLint flat config + Contract#3compliance gates (mirror of pw-os-v2 #289). Closes silent-brokenlintscript (eslint dep was never installed). 3 lint errors fixed at source: D18 hardcoded-haiku model literal → env-driven; useless-assignment on tool-use input parse; unusedcarg on/auth/register.
pw-infrastructure:
- PR
#169— chore(c5): remove pw-nexus / pw-onchain / pw-strategies Cloud Run placeholder services (-322 LOC; 14 cross-service IAM bindings → 3). Follow-up PRs queued for secrets/SA/DB cleanup per cross-workspace race rule. - PR
#170— chore(c3): drop Cloud SQL HA (REGIONAL → ZONAL).$148/mo savings ($1,776/yr). Investigation found 0 failover events in 21d of REGIONAL operation; pre-client status; SEC compliance unaffected; single-flag reversible. Apply outside business hours (3-5min downtime expected).
shared:
- PR
#23— docs(api): 7 new vendor docs (brave, fred, eia, edgar, etherscan, subgraph, mercury) + fmp.md refresh covering today's path corrections + env-trim defense. - This PR —
archive/cleanup; new authoritativePW-STATE-2026-05-09.md; CHANGELOG update.
Operational state changes
- pwdashboard.com fully retired: Cloudflare Worker confirmed deleted from account; repo archived on GitHub; DNS 301s to protocolwealthllc.com. Altruist redirect URI rotation pending Altruist support response (no production traffic gated on this).
- Spend baseline captured: $526/mo total GCP last 30d (Cloud SQL $297 + Memorystore $134 + Cloud Run $88). Trending to ~$378/mo after PR
#170applies. - Active GCP runtime collapsed: 8 Cloud Run services → 5 active (pw-api, pw-os, pw-portal, pw-api-webhooks, langfuse). Removed: pw-nexus, pw-onchain, pw-strategies (all
cloudrun/helloplaceholders).
Decisions logged
- C1 Memorystore tier change: declined; keep STANDARD_HA 5GB.
- C3 Cloud SQL ZONAL: approved + shipped (PR #170).
- C4 Cloud SQL right-size: held; revisit after 7-day Cloud Monitoring snapshot of post-ZONAL CPU/RAM.
- C5 Cloud Run cleanup: approved + foundation shipped (PR #169).
- S3 FRED key clean rotation: declined; code-side
.trim()accepted as the durable fix. - Custody terminology pinned: Turnkey for client wallet custody; Fordefi MPC + Coincover for firm treasury; Fortary evaluation-only.
Issues filed
- pw-api
#180— FMP/stable/etf-sector-weightings+/stable/etf-country-weightingsstill 404; needs live-key probe to determine path drift vs tier gating.
Memory updates
project_phase_x_status_2026_05_03.md— pw-onchain decommissioned. Architecture is GCP-only (no Fly/Upstash/Neon).project_advisor_portfolio_mvp_2026_04_29.md— onchain section noted as empty/unavailable until pw-api absorbs the wallets+snapshots flow.feedback_console_to_structured_log.md— apps/api useslogInfo/logWarn/logErrorfromlib/structured-log.ts; ESLint enforcesno-console=error.
Shared docs cleanup
- New:
PW-STATE-2026-05-09.md(authoritative, supersedes 4 prior dated state docs). - New: 7 vendor reference docs in
api/. - Refreshed:
api/fmp.md(was a 2026-03-22 stub referencing pw-nexus; now reflects pw-api reality). - Archived to
archive/state/: PW-STATE-2026-05-01/-02/-04, PW-STATUS-2026-05-01-EOD, MORNING-REPORT-2026-05-01, altruist-sync-audit-2026-05-01. - Archived to
archive/planning/: PW-NEXT-SCOPING-2026-05-01 (decisions executed), PW-PHASE-B-REVIEW-2026-04-25 (phase closed), ZDR-UPDATE-REVIEW-GUIDE (one-shot), PW-SUBPROCESSORS-v1_2-DRAFT (never published). - Archived to
archive/chat-audit/: pre-EOD chat-tools-audit-deltas-2026-05-02 (EOD version is superset; promoted to root). - New:
archive/README.md(index of what's in archive + when to consult).
2026-04-26
Theme: Tuesday signup link delivery for CCO (also serves as poster); cost-attribution labels rollout; cookie cutover Phase 1 JWKS diagnostic; canonical AGENTS.md landed in shared; session-end-hygiene skill registered.
Merged PRs
pw-os-v2 (Tuesday signup link build):
- PR
#65—audit_logfoundation (mirror of pw-api migration 0015) for forms/intake instrumentation. - PR
#66— forms data migration (legacy form rows → canonical schema). - PR
#68— hosted form routes (/f/<slug>) under advisor-public surface; CSP + secureHeaders preserved. - PR
#69— form seed forwebinar-newsletter-signup(the Tuesday link). - PR
#71— Inter font + form polish CSS. Discovered as no-op at runtime:secureHeadersmiddleware override stripped inline<style>blocks. PR shipped CSS but browsers receivedstyle-src 'self'. Followed by#72. - PR
#72— external/assets/form.css(actual rendering fix). Option-3 over (1) skippingsecureHeaders(would strip 12 protective headers) and (2) hash-allowlist (brittle). - PR
#70— closed as side-effect of#72.
pw-os-v2 (cookie cutover Phase 1):
- PR
#59— dual-issuer JWKS verification (RS256 via pw-api JWKS + HS256 fallback). Code-wired-and-deployed; inert untilPW_API_JWKS_URLset on pw-os Cloud Run (pw-infrastructure #91). HS256 fallback handles all session verification today.
shared:
- PR
#11— canonicalAGENTS.md+llms.txtlanded (mid-session merge during state-hygiene pass — scope adjustment for P2/P5/P7). - PR
#12— caching layer architecture design doc (CCO sign-off request filed; awaiting CCO — 6-point response). - PR
#13—session-end-hygieneskill +dependabot-majorsskill registration;skills/README.mdself-contained registry created (Option C — nopw-website/AGENTS.mdv-bump conflict per skill anti-pattern #3).
Issues filed
pw-infrastructure#91— finish step for cookie cutover Phase 1: setPW_API_JWKS_URLonpw-osCloud Run via terraform. Producer endpoint already public at pw-api-webhooks URL with valid RS256 public key.pw-infrastructure#90— cost-attribution labels into terraform (currently applied via additivegcloud --update-labels; needs codification).pw-os-v2#73— iframe-embed fix (post-Tuesday cleanup).pw-os-v2#74—Cache-Controlheader on/assets/*(post-Tuesday cleanup).- Internal
#94— Drizzle drift inpw-os-v2/CLAUDE.md. Closed via this hygiene pass (raw SQL migrations underapps/api/migrations/; no Drizzle). - Internal
#98—logInfoon every audit_log write (ops visibility; post-Tuesday cleanup). - Internal
#100—AGENTS.mdproducer-consumer verification pattern formalization (post-Tuesday cleanup). - (Plus held-batch items
#88,#89on pw-api half of Bundle B; gates on CCO Tuesday confirmation.)
Decisions
- Tuesday signup link gate verification (gate 17): CCO approved code-review + observed-behavior verification position. Code-review-plus-observed-behavior accepted as defensible verification standard.
- Cookie cutover Phase 1 framing corrected: doc claim "consumed in production" → "code-wired-and-deployed; configuration pending." Three separate verifications now required: (a) code merged, (b) deployed to revision, (c) env var wired. Pattern formalized as producer-consumer verification protocol in
protocols/SESSION-PROTOCOLS-2026-04-26.md§7. - Three-strikes rule formalized after polling-fix saga (3 broken attempts at canary
Ready=Truepolling): stop and reassess after 3 failed attempts at the same problem. Final fix: jq-based revision query (PR#69of pw-api-side polling work, Task#95closed). Evidence file §1. - Single-agent-per-repo rule formalized after M-tier stash collision. Evidence file §2.
- Status-doc accuracy pattern: counts must be verified by direct grep of source code, not derived from prior docs. Chat-tool count corrected from "14" → 13 by counting
registerTool({calls inapps/api/src/lib/tools/. Evidence §8. - Cost-attribution label schema locked:
app/env/cost_centerapplied to 9 production resources (7 Cloud Run services + Cloud SQL primary + Memorystore Redis) via additivegcloud --update-labels. BigQuery billing export enabled onpwllc-prod. Evidence §9. - Cache-as-not-books-and-records framing locked: Memorystore Redis is cache layer, SEC 17a-4 retention does NOT bind cache. Audit_log + Cloud SQL remain authoritative books-and-records. Awaiting CCO sign-off on caching architecture (6-point request open). Evidence §10.
- Memorystore Redis stays in architecture: confirmed needed for caching layer Phase 1 + future features.
- Drizzle drift correction:
pw-os-v2uses raw SQL migrations underapps/api/migrations/— no Drizzle, noschema.ts, no ORM. Directpg.Pool. Internal task#94closed.
Artifacts created
shared/protocols/SESSION-PROTOCOLS-2026-04-26.md— evidence record for ~10 protocols this session (cross-referencesAGENTS.mdfor the 6 already-canonical; full evidence for the new patterns).shared/CHANGELOG.md— this file.shared/compliance/cco-approvals/PW-CACHING-LAYER-CCO-REQUEST-2026-04-26.md— caching architecture CCO request artifact (awaiting CCO).shared/status/PW-STATUS-AND-CAPABILITIES-2026-04-26.md(P6, in progress) — evening-doc successor compiling today's work.pw-website/AGENTS.md v7.5.0 DRAFT(P7, in progress) — engineering-standards additions; not distributed to other PW repos this session.
Corrections applied (state-hygiene pass)
shared/status/PW-STATUS-AND-CAPABILITIES-2026-04-25-EVENING.md: surgical edits across TL;DR, §0 verdict, §1 cumulative claim, §4 D3 row, §4 Phase 1 bullet, §6 JWT canonical issuer table row, §6 lead sentence, "Verified-and-corrected" entry. Pattern: replace "Phase 1 shipped" / "consumed in production" with "code-wired-and-deployed; configuration pending; HS256 fallback handling all session verification." Reference:pw-infrastructure#91.pw-os-v2/CLAUDE.md: 4 Drizzle references replaced (raw SQL migrations underapps/api/migrations/; noschema.ts; no ORM;pg.Pooldirectly); chat-tool count "14" → 13 with provenance.pw-os-v2/README.md: chat-tool count "14" → 13 with provenance.- Memory anchor
project_chat_tools_wave_2026_04_23.md: "11 new chat tools" / "14 total" → 13 chat tools registered, verified 2026-04-26 againstregisterTool({calls. Catalog updated.
Cloud Run / infra
- 9 production resources cost-labeled (7 Cloud Run services + Cloud SQL primary + Memorystore Redis).
- BigQuery billing export enabled on
pwllc-prod. - pw-os Cloud Run revision serving PR
#59-merged consumer code (RS256+HS256 verifier deployed; inert per env-var gate above).
Held (preserved per §6 surface protocol; not started this session)
- Bundle B pw-api half (
#88, #89) — gates on CCO confirming Tuesday posts. - Email pipeline v1.0 full scope — same gate.
pw-os-v2#73(iframe-embed fix) — post-Tuesday.pw-os-v2#74(Cache-Controlon/assets/*) — post-Tuesday.- INET unification (#67) — post-Tuesday.
pw-infrastructure#90(labels into terraform) — post-Tuesday.pw-infrastructure#91(PW_API_JWKS_URLenv via terraform) — post-Tuesday.- Internal
#98(logInfo on every audit_log write) — post-Tuesday. - Internal
#100(AGENTS.mdproducer-consumer verification pattern) — post-Tuesday. - Caching layer Phase 1 build — gates on CCO §6 sign-off.
AGENTS.md v7.5.0distribution to all PW repos — draft only atpw-websitecanonical; full distribution deferred to separate session.pw-infrastructure/CLAUDE.mddrift, 84 stale local branches, 2 Dependabot alerts — deferred.
Older entries
This is the inaugural cross-repo CHANGELOG. Pre-2026-04-26 history is in:
- Per-repo
CHANGELOG.mdfiles where present. archive/status/CHANGELOG.mdfor status-doc revisions (wasstatus/CHANGELOG.mdpre-2026-05-14 reorg).git logacross the active PW estate.- Memory anchors under
~/.claude/projects/-home-nick-projects-pw/memory/.
Historical archives
Per the post-2026-05-14 reorg policy, this file carries the current quarter only. As entries age past the current quarter, they split per-month into archive/changelog/YYYY-MM.md. The split happens at quarter boundaries — at the first commit of the next quarter, the prior quarter's entries move out in a single PR.
Current state: Q2 2026 (April + May + June) in progress; nothing split out yet because the cross-repo CHANGELOG started 2026-04-26. The split convention is documented at archive/changelog/README.md.