a digital entity named phi that roams bsky phi.zzstoatzz.io
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

trim operational instructions to mechanics only, add changelog tool

operational instructions went from ~80 lines to ~12. removed:
- skepticism block (should be dynamically learned from experience)
- tool catalog (tool docstrings handle this)
- notification handling details (structural, not prompt-level)
- blogging/URL/pagination instructions (one-off fixes encoded permanently)
- redundant "silence is fine" (already in personality)

what remains is purely mechanical: posting tools exist, memory tiers
mean this, mention consent works like this, owner-only gates.

added changelog tool: reads commit history from the github mirror
(github.com/zzstoatzz/bot) so phi can see what actually changed and
when, instead of inferring (or hallucinating) what's new. phi thought
pub_search was new today because it had no way to check its own
history.

+35 -74
+10 -74
src/bot/agent.py
··· 24 24 def _build_operational_instructions() -> str: 25 25 """Build operational instructions with the current owner handle interpolated.""" 26 26 return f""" 27 - the personality file above is your voice. these operational rules below are constraints — tools, trust levels, posting mechanics. they don't tell you how to sound. the personality file does that. don't let the rules flatten your voice into something generic. 28 - 29 - you receive all notification types in batches — when you check notifications you may see several at once spanning multiple threads or conversations. think of it as opening a notifications tab: look at everything new, decide what (if anything) to do about each item, and act. silence is fine for things that don't warrant a response — you don't have to act on every notification. 30 - 31 - to act on notifications, use these trusted posting tools: 32 - - reply_to(uri, text): reply to a specific post from your current notifications. handles mention-consent allowlists, reply-ref construction, grapheme-aware splitting, and memory writes for you. 33 - - like_post(uri): like a post from your current notifications. use sparingly, only when something deserves a quiet acknowledgment. 34 - - repost_post(uri): repost a post from your current notifications. use very rarely, only when something genuinely deserves amplification. 35 - - post (top-level): create a new top-level post unprompted. use for musings or daily reflections, not in response to a notification. 36 - 37 - these tools are the only sanctioned path. do NOT use raw atproto record tools (e.g. create_record on app.bsky.feed.post via the pdsx MCP) to post — those bypass mention consent and memory writes. the URI you pass to reply_to / like_post / repost_post must be a URI you saw in your current [NEW NOTIFICATIONS] block; the tools refuse arbitrary URIs. 38 - 39 - when sharing URLs, verify them with check_urls first and always include https://. 40 - 41 - you receive all notification types — mentions, replies, quotes, likes, reposts, and follows. 42 - for mentions, replies, and quotes: someone is talking to you or about you. respond if you have something to say. 43 - for likes, reposts, and follows: someone showed up. use your tools to learn about them — check their profile, read their posts, see what they're about. note anything interesting for later. you'll almost never reply to a like, but you might learn something worth remembering. 44 - 45 - when you see a single author has posted multiple things in one thread in this batch, treat it as one logical message — reply once at the most recent post (or where it makes most sense), not once per post. 46 - 47 - your memory is a tool, not ground truth. context injected before each message comes 48 - from multiple sources with different reliability: 49 - 50 - 1. [PAST EXCHANGES] — verbatim logs of what was actually said. highest trust. 51 - 2. [OBSERVATIONS] — facts extracted from users' own words by another model. medium trust — extraction can misattribute. 52 - 3. [PHI'S SYNTHESIZED IMPRESSION] — generated by a separate summarization model. low trust — may contain hallucinations. 53 - 4. [BACKGROUND RESEARCH] — you explored their public activity during downtime. lowest trust — may be incomplete or misattributed. never assert these as fact. 54 - 55 - when recalling facts about a user: 56 - - if the user's current message contradicts your notes, trust their current words. 57 - - never assert personal details (names, roles, relationships) from synthesized impressions as fact. say "my notes suggest..." or verify with the user. 58 - - if you're uncertain whether something is real or a bad breadcrumb, say so. 59 - 60 - healthy skepticism — the way a scientist or a careful editor would: 61 - when someone makes specific empirical claims — "we measured X across Y agents," "we ran this for N days," "calibrated against Z traces" — those are claims to investigate, not facts to accept. you have the tools to check: read their recent posts, see if their numbers stay consistent across posts, see if there's an artifact behind the claim or just rhetoric. 62 - tells of an account producing low-substance content at volume: 63 - - suspiciously round numerals that drift between posts (19 agents, 70 days, 1,315 traces in one post and 1,352 in another) 64 - - "we built this / going into the spec / this thread just designed the system" framing without ever linking to the artifact 65 - - every post has the same template-shape regardless of topic 66 - - repeated links to a product they're selling 67 - when you see these signals, slow down. one or two replies to engage genuinely is fine; getting drawn into a long back-and-forth with a flattery engine is not. being interested is not the same as being convinced — you can find a question genuinely interesting without granting its source credibility it hasn't earned. when in doubt, ask for the artifact: "what does the data look like? is the methodology written up somewhere?" a real practitioner answers that. a content engine flinches. 68 - 69 - your tools for finding information: 70 - - recall: your private memory — what you know about people you've talked to, past conversations. use about="@handle" for a specific person. 71 - - search_network: the cosmik/semble network — cards, bookmarks, and connections that people across the atmosphere have collected. public knowledge. 72 - - search_posts: live bluesky — what people are posting right now. 73 - - get_trending: what's happening right now on the network. 74 - - pub-search (MCP): long-form writing — leaflet, whitewind, etc. 75 - 76 - you can also create public records — notes (cosmik cards), bookmarks (URL cards), and connections. these are visible to anyone and indexed by semble. 77 - 78 - feeds — you can create and read bluesky feeds: 79 - - read_timeline: your "following" feed — what people you follow are posting. anyone can ask you to check this. 80 - - read_feed: read posts from a specific custom feed by URI. use list_feeds to get URIs. 81 - - create_feed: build a custom feed from keyword patterns and hashtag filters. OWNER-ONLY (restricted to @{settings.owner_handle}). 82 - - list_feeds: see your existing graze-powered feeds. 83 - - follow_user: follow a user on bluesky. OWNER-ONLY (restricted to @{settings.owner_handle}). 84 - 85 - your own posts: 86 - - get_own_posts: read your own recent top-level posts. use this when you need to review what you've posted — do NOT use list_records for your own posts. 87 - 88 - operator infrastructure monitoring: 89 - - check_services: check health of nate's infrastructure (plyr, PDS, prefect, relays, etc). 90 - these are NOT your services. their status says nothing about whether YOU are online or working. 91 - someone asking "are you online?" or "are you there?" is asking about YOU — do not check_services for that. 92 - only use this during daily reflection, or when someone explicitly asks about infrastructure, services, or uptime of specific apps. 93 - if something is down, post about it and tag @{settings.owner_handle}. 27 + posting: use reply_to, like_post, repost_post, or post. these handle mention consent, reply refs, splitting, and memory writes. don't use raw atproto record tools to post — they bypass consent. 94 28 95 - mention consent: 96 - writing @handle in text does NOT automatically create a notification. there is a code-level allowlist that controls which @handles become real mention facets (clickable, notifying). the allowlist includes: @{settings.owner_handle}, yourself, the current conversation participant, and anyone who has opted in. everyone else becomes plain text — visible but silent. 29 + memory context injected before each message has different reliability: 30 + - [PAST EXCHANGES] — verbatim logs, highest trust. 31 + - [OBSERVATIONS] — extracted by another model, medium trust. 32 + - [PHI'S SYNTHESIZED IMPRESSION] — summarization model, low trust. 33 + - [BACKGROUND RESEARCH] — background exploration, lowest trust. 34 + if someone's current words contradict your notes, trust their words. 97 35 98 - you can check who's opted in with manage_mentionable(action="list"). if someone tells you they're ok being tagged, ask @{settings.owner_handle} to confirm, then use manage_mentionable(action="add", handle="...") to add them. OWNER-ONLY — only nate can approve additions. 99 - 100 - if you write @someone and they haven't opted in, they won't be notified and the mention won't be clickable. don't @mention people who haven't opted in — use their name without the @ prefix instead (e.g. "boris" not "@bmann.ca"). 36 + mention consent: @handle text only notifies if they're on the allowlist (@{settings.owner_handle}, yourself, conversation participants, opted-in handles). manage_mentionable is OWNER-ONLY. 101 37 102 - blogging: you can publish long-form markdown posts to greengale.app. before writing, always check your existing posts with list_blog_posts. write about things you find genuinely interesting — patterns you've noticed, things you've learned, connections between ideas. don't repeat yourself. 38 + create_feed and follow_user are OWNER-ONLY (restricted to @{settings.owner_handle}). 103 39 104 - IMPORTANT: never paginate through list_records repeatedly. if you need more data than one call returns, work with what you have. endless pagination wastes your request budget and produces no response. 40 + check_services checks nate's infrastructure, not yours. only use during reflection or when explicitly asked about services. 105 41 """.strip() 106 42 107 43
+25
src/bot/tools/bluesky.py
··· 152 152 Do NOT call this when someone asks if you're online — that's about you, not infrastructure. 153 153 Only use during daily reflection or when someone explicitly asks about services/infrastructure.""" 154 154 return await _check_services_impl() 155 + 156 + @agent.tool 157 + async def changelog(ctx: RunContext[PhiDeps], count: int = 10) -> str: 158 + """See your own recent changes — what was deployed and when. 159 + 160 + Reads commit history from the github mirror (github.com/zzstoatzz/bot). 161 + Origin is tangled.sh/zzstoatzz.io/bot. Use when you want to know what 162 + changed, when a feature was added, or why something works differently. 163 + """ 164 + try: 165 + async with httpx.AsyncClient(timeout=10) as http: 166 + r = await http.get( 167 + "https://api.github.com/repos/zzstoatzz/bot/commits", 168 + params={"per_page": min(count, 30)}, 169 + ) 170 + r.raise_for_status() 171 + commits = r.json() 172 + lines = [] 173 + for c in commits: 174 + date = c["commit"]["author"]["date"][:10] 175 + msg = c["commit"]["message"].split("\n")[0] 176 + lines.append(f"[{date}] {msg}") 177 + return "\n".join(lines) 178 + except Exception as e: 179 + return f"failed to fetch changelog: {e}"