Merge profile wallet connect implementation from coder

By refactorer.
This commit is contained in:
Chris Troutner
2026-09-03 16:08:13 -07:00
8 changed files with 152 additions and 8 deletions
+13
View File
@@ -49,6 +49,19 @@ distinct from per-task verification results, which live in
to keep the worker copies tiny. This cut a notifications-query mutation run
from 20+ minutes to ~2 minutes.
- **Which directories bloat and how to keep them clean.** The two directories
that grow without bound are `<component>/tmp/acceptance` (LevelDB dirs from
acceptance runs, up to ~1.5G) and `<component>/target/mutation-workers`
(per-run worker copies, up to ~14G). Both are gitignored build artifacts.
Clean them before any mutation run:
```bash
rm -rf psf-memo-db/tmp/acceptance psf-memo-db/target/mutation-workers \
psf-memo-client/tmp/acceptance psf-memo-client/target/mutation-workers
```
`architect-startup.sh` now checks these and reports them as `[FAIL]` when
they exceed a size threshold, so a bloated dir is caught before it slows a
mutation run.
- **`memo-db.js` (client HTTP adapter) is excluded from mutation testing.** It
uses ESM + a directory import (`../config`) that is only resolvable via
react-scripts/webpack, so it cannot be loaded under plain `node --test`. Its
+12
View File
@@ -48,6 +48,18 @@ Parse survivors with a small python one-liner over `d['results']` (filter
`--workers 8` and `--status-interval` so progress is visible.
- Run verification sequentially (never concurrent with acceptance generation).
## Keep build dirs clean (mutation speed)
`mutate4javascript` copies the whole project (including `tmp/`) into each worker.
Stale `tmp/acceptance` (LevelDB dirs) and `target/mutation-workers` can bloat to
~1.5G and ~14G, making the worker copy alone many GB and mutation runs appear to
hang. `architect-startup.sh` now flags any of these over 100MB as `[FAIL]`.
Clean them before any mutation run:
```bash
rm -rf psf-memo-db/tmp/acceptance psf-memo-db/target/mutation-workers \
psf-memo-client/tmp/acceptance psf-memo-client/target/mutation-workers
```
This cut a notifications-query mutation run from 20+ minutes to ~2 minutes.
## Refactorer forwards your own review commit back
After you send a `priority: 00` review commit to coder+refactorer, the refactorer
merges and forwards that SAME commit back to you. It is a no-op ("Already up to
@@ -11,6 +11,7 @@ import MemoDb from '../../../services/memo-db'
import MemoFollow from '../../../services/memo-follow'
import MemoMute from '../../../services/memo-mute'
import ProfilePage from '../../../services/profile-page'
import { getViewerAddress } from '../../../services/profile-wallet'
import PostReplyCount from '../../post-reply-count'
import LikeButton from '../../post-feed/like-button'
import PostThreadModal from '../../post-thread-modal'
@@ -75,6 +76,10 @@ function Profile (props) {
setThreadTxid(null)
}
const wallet = appData?.wallet || null
const appProfiles = appData?.profiles || null
const myAddr = getViewerAddress(appData)
const runAction = async (method, failMsg) => {
if (!profilePage || busy) return
setBusy(true)
@@ -98,12 +103,11 @@ function Profile (props) {
try {
const memoDb = new MemoDb()
const myAddr = appData?.wallet?.walletInfo?.cashAddress || null
const memoFollow = myAddr
? new MemoFollow({ wallet: appData.wallet, profiles: appData.profiles })
const memoFollow = myAddr && wallet
? new MemoFollow({ wallet, profiles: appProfiles })
: null
const memoMute = myAddr
? new MemoMute({ wallet: appData.wallet, profiles: appData.profiles })
const memoMute = myAddr && wallet
? new MemoMute({ wallet, profiles: appProfiles })
: null
const page = new ProfilePage({ memoDb, addr, myAddr, memoFollow, memoMute })
@@ -132,7 +136,7 @@ function Profile (props) {
setError('Missing profile address')
setLoading(false)
}
}, [addr, appData?.wallet, appData?.profiles])
}, [addr, myAddr, wallet, appProfiles])
const showFollowButton = profilePage && profilePage.canFollow() && !profilePage.isFollowing()
const showUnfollowButton = profilePage && profilePage.canFollow() && profilePage.isFollowing()
@@ -0,0 +1,19 @@
/*
Wallet address selection for the profile page.
The React app stores the authenticated wallet address in two places:
`appData.bchWalletState.cashAddress` (reactive state updated as the wallet
initializes) and `appData.wallet.walletInfo.cashAddress` (the wallet object
itself). Prefer the reactive state so the profile page re-loads when the
address becomes available.
*/
'use strict'
function getViewerAddress (appData) {
return appData?.bchWalletState?.cashAddress ||
appData?.wallet?.walletInfo?.cashAddress ||
null
}
module.exports = { getViewerAddress }
@@ -0,0 +1,60 @@
/*
Unit tests for the profile wallet address selector.
The profile page must show follow/mute controls only when the viewer's
wallet address is known. The React app tracks the loaded address both in the
wallet object and in `bchWalletState`; the selector prefers the reactive
state so the profile reloads when the address becomes available.
*/
'use strict'
const test = require('node:test')
const assert = require('node:assert/strict')
const { getViewerAddress } = require('../../src/services/profile-wallet')
function makeAppData ({ bchCashAddress, walletCashAddress } = {}) {
return {
bchWalletState: {
cashAddress: bchCashAddress
},
wallet: walletCashAddress
? { walletInfo: { cashAddress: walletCashAddress } }
: null,
profiles: {}
}
}
test('getViewerAddress prefers bchWalletState.cashAddress', () => {
const appData = makeAppData({
bchCashAddress: 'bitcoincash:state-address',
walletCashAddress: 'bitcoincash:wallet-address'
})
assert.equal(getViewerAddress(appData), 'bitcoincash:state-address')
})
test('getViewerAddress falls back to wallet.walletInfo.cashAddress', () => {
const appData = makeAppData({
bchCashAddress: undefined,
walletCashAddress: 'bitcoincash:wallet-address'
})
assert.equal(getViewerAddress(appData), 'bitcoincash:wallet-address')
})
test('getViewerAddress returns null when no wallet is loaded', () => {
const appData = makeAppData({})
assert.equal(getViewerAddress(appData), null)
})
test('getViewerAddress returns null when wallet lacks an address', () => {
const appData = {
bchWalletState: {},
wallet: { walletInfo: {} },
profiles: {}
}
assert.equal(getViewerAddress(appData), null)
})
+3 -2
View File
@@ -359,6 +359,7 @@ At the end of each session, update this file:
- Note the current `master` HEAD commit.
- State the next feature to work on.
Current `master` HEAD: `d3903fa` (backlog cleared of all previously listed
features; direction set to front-end improvements to `psf-memo-client`).
Current `master` HEAD: `d99eda3` (merged architect's notifications refactor +
startup bloat check; verified db 315 passing + lint clean, client 239 passing +
lint clean + build succeeds).
Next action: **ask the user for the next front-end improvement to spec**.
+21
View File
@@ -73,6 +73,27 @@ for c in psf-memo-client psf-memo-db psf-memo-indexer; do
&& ok "$c runner-worker" || bad "$c runner-worker"
done
echo "== Bloated build dirs (slow mutation copies) =="
# tmp/acceptance and target/mutation-workers are gitignored build artifacts
# that mutate4javascript copies into every worker. If they grow large, the
# worker copy alone can be many GB and mutation runs appear to hang. Flag any
# dir over the threshold so it can be cleaned before a mutation run.
BLOAT_KB=102400 # 100 MB
for d in psf-memo-client/tmp/acceptance psf-memo-client/target/mutation-workers \
psf-memo-db/tmp/acceptance psf-memo-db/target/mutation-workers \
psf-memo-indexer/tmp/acceptance psf-memo-indexer/target/mutation-workers; do
if [ -d "$d" ]; then
size_kb=$(du -sk "$d" 2>/dev/null | awk '{print $1}')
if [ -n "$size_kb" ] && [ "$size_kb" -gt "$BLOAT_KB" ]; then
bad "$d is ${size_kb}KB (clean with: rm -rf $d)"
else
ok "$d clean"
fi
else
ok "$d absent"
fi
done
echo
echo "Result: $pass ok, $fail fail"
[ "$fail" -eq 0 ]
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# Wrapper for the Babashka APS gherkin-parser task.
# Delegates to the project-local APS checkout under .swarmforge/tools/aps.
set -euo pipefail
if [ "$#" -ne 2 ]; then
echo "usage: gherkin-parser <feature-file> <json-output>" >&2
exit 2
fi
FEATURE_FILE="$(realpath -m "$1")"
JSON_OUTPUT="$(realpath -m "$2")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APS_DIR="$(cd "$SCRIPT_DIR/../../.swarmforge/tools/aps" && pwd)"
cd "$APS_DIR"
exec bb gherkin-parser "$FEATURE_FILE" "$JSON_OUTPUT"