Skip to content

groups: add group-level custom blob field - #6209

Open
arthyn wants to merge 31 commits into
developfrom
hunter/tlon-6274-groups-add-group-level-opaque-blob-field-custom-metadata
Open

groups: add group-level custom blob field#6209
arthyn wants to merge 31 commits into
developfrom
hunter/tlon-6274-groups-add-group-level-opaque-blob-field-custom-metadata

Conversation

@arthyn

@arthyn arthyn commented Jul 30, 2026

Copy link
Copy Markdown
Member

Summary

Adds an opaque, admin-writable blob=(unit @t) field to $group in the %groups agent — a group-level analogue of the channel-level custom meta field — for client-owned group config and bot/automation metadata, plus full frontend plumbing.

Fixes TLON-6274

CI will fail until we cut a new pier archives, waiting to do that until its confirmed (or we could PR having the harnesses pull in latest desk changes)

Changes

Backend

  • blob=(unit @t) on $group; %blob variants on a-group/c-group/u-group/r-group; new ++ v11 dependency closure in sur/groups-ver
  • Host setter (admin-gated, 256KB size cap, idempotent) and client apply; responses ride a new /v3/groups lane (group-response-3) emitted first, with groups-conv downgraders keeping v2/v1/v0 blob-free (same playbook as %active-channel / 39458f2)
  • Actions ride a new strict group-action-5 mark (a-groups:v11); group-action-4 stays frozen at v8 so older clients are unaffected
  • State migration %11 → %12 (blob defaults to ~, pub logs re-walked for embedded %create groups); groups negotiate version %2 → %3 (update/log/command wire shapes change), bumped in %groups, %channels, and %channels-server
  • New read surfaces: /x/v3/{groups,changes,groups/…,ui/groups/…}, /x/v4/init, and groups-ui /v9/init + /v10/changes (activity intentionally stays v8 — the unconsumed init-8/activity-v9 bump is not bundled in); new marks group-3, groups-3, group-ui-3, group/changed-groups-3, group/response-3, ui/init-9 registered in rail/discipline/mark-warmer

TypeScript

  • Wire types for the %blob action/response; updateGroupBlob poke via groupAction5() tracked on /v3/groups; a third subscribeGroups subscription filtered to blob events (.catch for older backends)
  • getGroup/getGroups moved to the v3 scries; init → /v9/init, changes → /v10/changes
  • groups.blob db column (migration regenerated), insertGroups conflict set, editGroupBlob sync case, optimistic updateGroupBlob store action

How did I test?

  • ./backend/run-tests.sh fully green on a fresh pier: 317 OK, Unit tests passed ✅ and Aqua tests passed ✅, including a new +test-c-group-blob (admin set/clear, idempotence, non-admin/non-member nacks)
  • pnpm -r tsc clean workspace-wide; api + shared vitest green, including new tests for the toV1GroupsUpdate blob arm and the editGroupBlob sync handler
  • Live smoke test on the ~rapsed dev moon: %11 → %12 migration ran cleanly against real group state; blob set + clear round-trips via group-action-5; /v3 scries, /v9/init, and /v10/changes carry the blob while /v2 surfaces stay blob-free

Risks and impact

  • Safe to rollback without consulting PR author? (Yes)
  • Affects important code area:
    • Onboarding
    • State / providers
    • Message sync
    • Channel display
    • Notifications
    • Other: %groups agent state migration + negotiate version bump

Notes: the negotiate bump pauses groups sync between ships until both sides OTA (same as the request-timestamps bump, 027013b). New frontend requires the new desk (/v3 scries and /v9/init are a hard cutover, matching prior init bumps). %create responses on v1/v2 are blob-stripped; a just-joined group briefly lacks its blob until init/changes/getGroup or the next blob update.

Rollback plan

Revert the two commits. The desk state migration is forward-only (%12), so rolled-back code must not ship to ships that already ran it — roll forward with a fix instead for any ship that has OTA'd.

Screenshots / videos

N/A — backend field + API plumbing; no UI changes.

🤖 Generated with Claude Code

arthyn and others added 2 commits July 30, 2026 10:59
Add an opaque, admin-writable blob=(unit @t) to $group for
client-defined metadata, mirroring the channel-level custom meta
lane. New v11 types carry %blob on a-group/c-group/u-group/r-group;
responses ride a new /v3/groups lane (group-response-3) with conv
downgraders keeping v2/v1/v0 blob-free. Actions ride a new strict
group-action-5 mark; group-action-4 stays frozen at v8. State bumps
%11 -> %12 (blob defaults to ~, pub logs re-walked) and the groups
negotiate version bumps %2 -> %3 since the update/log/command wire
shapes change. New read surfaces: /x/v3 group scries, /x/v4/init,
/v9/init and /v10/changes on %groups-ui.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wire types for the %blob action/response, updateGroupBlob poke via
group-action-5 tracked on /v3/groups, a third groups subscription
filtered to blob events, getGroup/getGroups moved to the v3 scries,
init/changes bumped to /v9/init and /v10/changes, a groups.blob db
column, editGroupBlob sync handling, and an optimistic
updateGroupBlob store action.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jul 30, 2026

Copy link
Copy Markdown

TLON-6274

arthyn and others added 2 commits July 30, 2026 11:46
CI's tsc config rejects dynamic import() (TS1323).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
getGroups/getGroup, /v9/init, and /v10/changes 404 against backends
that haven't shipped the group blob; retry the v2/v7/v8 paths, whose
responses are identical minus the blob. Softens the hard cutover for
not-yet-OTA'd nodes and lets the bot e2e harness run against its
archived ships.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arthyn
arthyn force-pushed the hunter/tlon-6274-groups-add-group-level-opaque-blob-field-custom-metadata branch from 87d3a61 to a13c04e Compare July 30, 2026 16:58
arthyn and others added 4 commits July 30, 2026 17:23
Joining a group whose blob predates the join emits %create on both
lanes: v3 carries the blob, v1 strips it. Process v3 %create in the
blob subscription and treat a missing blob key as "don't touch" in
the group upsert (explicit null still clears), so the v1 copy that
lands second no longer clobbers the blob to null.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep the current action mark first in the dispatch; the deprecated
arms read as a descending tail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
%group-ui-3 is the singular per-group mark (/x/v3/ui/groups/$/$) but
sat after %groups-ui-2, reading as though it belonged to the plural
groups-ui family. Move it beside %group-3. The strict list is gassed
into a set, so ordering is cosmetic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
%group-ui, -1 and -2 were never in the discipline strict list, though
they existed and were rail-registered when the list was written. That
left the guardrail protecting the plural groups-ui family (which has
no consumer) while the singular family read by the frontend getGroup
and ted/group/create* went unchecked. Add them alongside the new
%group-ui-3.

Adding is safe on upgrade: check-marks iterates the stored mark map,
so a newly listed mark isn't compared until the load after it is
first snapshotted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@blacksmith-sh

This comment has been minimized.

arthyn and others added 3 commits August 3, 2026 11:01
changesApi scried /v10/changes while annotating the result ChangesV8,
and initApi scried /v9/init as GroupsInit7. Both were structurally
right (the blob rides inside GroupV7) but read a version behind.

Follow the convention already in urbit/ui.ts — interface named for the
newest version, older ones aliased to it — so the primary types become
ChangesV10 and GroupsInit9 and the fallback branches keep the older
names that match the paths they actually scry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The client opened /v1 unfiltered plus filtered /v2 and /v3 concurrently,
so adding %create to the v3 filter meant every create was handled twice
and the blob upsert guard became load-bearing mitigation for that
collision.

r-group:v11 is a superset of v9 and v10, so subscribe to /v3/groups
alone and handle everything from it. Only when that watch path is
unavailable do we open the legacy v1 + v2 pair. No event is handled
twice, and the undefined-vs-null blob distinction goes back to being
defensive rather than propping up a race.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
groupAction5 was annotated GroupActionV4, and because blob had been
added to the shared inner GroupAction type, all 25 groupAction4 call
sites also accepted a blob action — which mark group-action-4 cannot
carry, since its dejs is a-groups:v8. That typechecked and would have
nacked at runtime.

Parameterize the envelope, which is identical at both versions, on the
a-group payload: GroupActionV4 keeps the blob-free GroupAction while
GroupActionV5 adds it. GroupActionV4 now rejects a blob action at
compile time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@blacksmith-sh

This comment has been minimized.

arthyn and others added 5 commits August 3, 2026 12:14
The desk ships ahead of the app, so a client newer than its backend is
not a state we support; the v2/v7/v8 fallbacks protected a direction
that cannot happen and had no precedent here (prior init bumps were
bare path swaps). Removing them also deletes the one code path that
was never verified against a real old ship.

The backend keeps its v0/v1/v2 downgrade lanes untouched — those serve
old clients on a new desk, which is the normal state.

Also repoints the five tracked group pokes from /v2/groups to
/v3/groups. A trackedPoke only resolves when its watch endpoint's own
subscription receives an event, so after collapsing to one lane they
were watching a path nothing subscribes to and every group mutation
would have hung 20s then thrown. Adds a test that asserts tracked
pokes watch a subscribed lane.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
It restated a ten-line function rather than asserting anything the code
does not already say, and would need editing on every version bump. The
tracked-poke test still covers the lane invariant that actually matters,
since that one relates two independent places in the file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Audited every version-tagged type the blob change touched, directly or
transitively:

  V1GroupResponse -> GroupResponse   named for the /v1 path we no longer
                                     read, on a type that is now v11, and
                                     the only versioned name among its ten
                                     GroupResponse* siblings
  GroupV7  -> GroupV11               gained blob
  GroupsV7 -> GroupsV11              map of the above
  toV1GroupsUpdate  -> toGroupsUpdate
  toClientGroupV7   -> toClientGroup      one wire version reaches these
  toClientGroupsV7  -> toClientGroups     now, so drop the suffix
  extractChannelReadersFromV7Groups -> extractChannelReadersFromGroups

Deletes ChangesV8, ChangesV7, GroupsInit7 and GroupsInit6, which were
aliases kept only for the fallbacks; the surviving fixture casts point
at GroupsInit9.

Left alone: GroupChannelV7, SectionV7, SectionsV7 and GroupPreviewV7
describe shapes that are unchanged from v7 through v11, so their names
are accurate and renaming them would make them wrong.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Version arithmetic like "v10 changes is v8 plus the group blob" is
already carried by the type names and the paths beside them. Two of
these were also stale, still citing the v1/v2 fallbacks that were
removed.

What stays is the non-obvious: why group-action-4 would nack a blob,
why undefined and null differ in the group upsert, why tracked pokes
must watch the subscribed lane, and the Hoon type names each wire
type maps to, which TypeScript cannot show.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The client already hard-requires the new desk — getGroups, getGroup and
subscribeGroups all read /v3 with no fallback — so keeping action-4
bought no compatibility while leaving a live footgun about which mark
can carry which action.

Moves all 29 pokes to group-action-5: the 25 in groupsApi via a single
merged groupAction() helper, plus groupNavigationBatchUpdate, the
flag-content poke in postsApi, four raw pokes in tlon-skill, and the
web mock. The Hoon %group-action-4 arm stays for older app builds.

With one live mark the GroupActionEnvelope<T> parameterization has no
purpose, so it collapses back to a concrete GroupActionV5 over a single
GroupAction that includes blob; GroupActionV4 and GroupActionV5Data are
gone.

Adds dejs tests in tests/lib/groups-json proving the migrated actions
still parse at v11 — meta, delete, entry/privacy and leave — alongside
blob set and clear. v11's a-group dejs is v8's arm list plus
blob+(mu so), with identical sub-parsers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arthyn
arthyn marked this pull request as ready for review August 3, 2026 22:29
@blacksmith-sh

This comment has been minimized.

@mikolajpp mikolajpp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good.

Some nits in the comments. Appreciate the less-verbose coding style :-)

Comment thread desk/lib/mark-warmer.hoon Outdated
Comment thread desk/app/groups.hoon
Comment thread desk/app/groups.hoon
Comment thread desk/app/groups.hoon
Comment thread desk/app/groups.hoon Outdated
Comment thread desk/app/groups.hoon
Comment on lines 1321 to 1329
[%x ver=?(%v0 %v1 %v2) %light %groups ~]
=/ groups-9=groups:v9:gv
%- ~(run by groups)
|= [=net:v9:gv =group:v9:gv]
(drop-seats:group:v9:gc group our.bowl)
|= [=net:g =group:g]
(v9:group:v11:gc (drop-seats:group:v11:gc group our.bowl))
?- ver.pole
%v0 ``groups+(~(run by groups-9) v2:group:v9:gc)
%v1 ``groups-1+(~(run by groups-9) v5:group:v9:gc)
%v2 ``groups-2+groups-9

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this endpoint in use by anything? If not, we should mark it for removal later. Otherwise it needs updating too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked — nothing reads it: no reference to /x/vN/light/groups or the /x/groups/light alias anywhere in packages/, apps/, or desk/. Deleted outright rather than marked, in the stacked #6224.

Comment thread desk/app/groups.hoon Outdated
Comment on lines +1342 to +1343
%v1 ``groups-ui-1+(~(run by groups) group-ui:v5:group:v11:gc)
%v2 ``groups-ui-2+(~(run by groups) group-ui:v9:group:v11:gc)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above, we don't expose v11 here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same story: the only reference to the plural /x/vN/ui/groups map was a request-timeout test using the path as filler. The in-desk create threads read the per-flag /gx/groups/vN/ui/groups/<ship>/<name>, which is a different arm and stays. So rather than expose v11 there, #6224 deletes the plural map. Left unresolved for your call.

Comment thread desk/app/groups.hoon Outdated
Comment thread desk/app/groups.hoon Outdated
@jamesacklin

Copy link
Copy Markdown
Member

Seems good to me, my stuff could write to this easily

- delete lib/mark-warmer.hoon and its two imports; on kelvin 408 all
  marks build by default, so pre-building them is redundant
- deflate %group-action-4 into %group-action-5 with the $(+< ...)
  pattern %group-action-3 already uses, rather than duplicating the
  dispatch; a-groups:v8 nests under v11
- add %group-action-4 to the strict mark list, which it was missing
- drop two casts the typed rails make unnecessary
- give both downgrade lanes in +go-response the same ?~ shape; v10's
  nil case (%blob) is a subset of v9's (%blob, %active-channel), so
  returning early on v2 cannot skip a v1 send

Verified on a moon: an old %group-action-4 poke applies through the v5
handler, and blob still reaches /v3 while /v2 stays blob-free.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3d6e73fce

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/shared/src/db/migrations/0000_late_micromacro.sql
arthyn and others added 3 commits August 4, 2026 14:56
zod, ten and mug are re-archived from ships running this branch's desk,
so the e2e harnesses can talk to a client that reads /v3 with no
fallback. Verified the archives carry it: mar/group-3, mar/group/action-5
and blob=(unit @t) in sur/groups.hoon. ~bus stays at rube-bus9, still
deliberately outdated for protocol mismatch testing.

Named after what they contain rather than a position in the numbered
lineage, via a new ARCHIVE_TAG override in archive-piers.sh. Note the
tag is now required for the next bump: the script errors out rather than
silently minting rube-zod1.tgz when it meets a non-numeric manifest
entry.

Also updates the bot e2e compose, which archive-piers.sh does not touch
and which is what Shared E2E actually reads — including the pinned md5
and byte size for each ship, cross-checked against gsutil stat.

Old archives stay in the bucket, so openclaw's dev compose and its
rube-27-calibrated gateway-status test keep working untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neither surface has a consumer: nothing anywhere reads /x/vN/light/groups
or the deprecated /x/groups/light alias, and the only reference to the
plural /x/vN/ui/groups map was a request-timeout test using the path as
arbitrary filler (the in-desk create threads read the per-flag path,
which stays). Rather than carry them forward to v3, remove the peek arms
and their scry declarations.

Repoints that timeout test at /v3/groups so it does not name a deleted
endpoint.

Leaves the now-unproduced %groups-ui{,-1,-2} marks in place: dropping a
strict mark is a one-way door and keeping them costs nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Case 08's first readStatus() died with a terminal 404 as soon as this
branch regenerated the e2e piers. The scry it reads moved months ago: the
steward refactor (3b4d55b and follow-ups) relocated gateway liveness
state into %steward's gateway module and reduced %gateway-status to a
poke-only proxy whose on-peek is a stub. The case only kept passing
because it was reading a rube-27 pier baked from a pre-refactor desk, so
any pier refresh would have surfaced this.

Point the read path at %steward /x/v1/gateway/status. The write path is
untouched — the plugin still pokes %gateway-status-action-1, which the
proxy forwards on as steward-gateway-action-1.

decodeGatewayStatus needs no change: ga-peek serves the same
[status (unit @da)] tuple. Verified against a live ship on the current
desk — /v1/gateway/status cues and decodes to {status, leaseUntil},
/v1/gateway/owner-activity decodes via decodeDa, and the old
%gateway-status path returns the same terminal 404 seen in CI.

Also moves openclaw's dev compose to the group-blob archives. Both it and
tlon-bot-e2e's compose run test/cases/, so leaving dev on rube-27 would
trade a CI failure for a `pnpm test:integration` failure. md5s and byte
sizes cross-checked against the x-goog-hash headers on bootstrap.urbit.org.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6692f1f6f3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/shared/src/db/migrations/meta/_journal.json
Comment thread packages/shared/src/store/groupActions.ts
Comment thread apps/tlon-web/rube/archive-piers.sh
Comment thread apps/tlon-web/rube/archive-piers.sh
updateGroupBlob: the host's %blob arm emits nothing when the value is
unchanged, so trackedPoke waited out its 20s timeout and then rolled
back a write that was already correct. Return early instead. Adds tests
covering unchanged, both-empty, changed and cleared; the two no-op cases
fail without the guard.

archive-piers.sh: the version guard added with ARCHIVE_TAG never fired.
print_error/print_info write to stdout, and `local x=$(f)` returns the
status of local rather than f, so the caller captured the diagnostics as
the version string and would have built an archive name out of the error
text. Redirect those two prints to stderr, matching archive_pier's
existing convention, and split the declaration from the assignment so
the failure aborts.

update_dockerfile matched only rube-{ship}[0-9]*.tgz. That worked for
the numeric-to-tagged transition but would silently skip a second tagged
run, leaving the Dockerfile on a stale tarball. Match any suffix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a085007743

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread desk/app/groups-ui.hoon Outdated
arthyn and others added 4 commits August 5, 2026 16:00
…ld-custom-metadata' into hunter/tlon-6274-remove-dead-group-scry-surfaces
Deleting `/x/groups/light` and the `%light %groups` arm broke CI: rube's
`checkGroupsAppHealth` probes `/~/scry/groups/groups/light.json`
(apps/tlon-web/rube/index.ts:1205), so every ship failed its readiness
check and both the parallel E2E job and the production build smoke test
timed out in "Setting up ships".

Three more live callers depend on the same deprecated alias:

  - apps/tlon-mobile/ios/Shared/Networking/PocketChatAPI.swift:17
  - apps/tlon-mobile/android/.../api/TalkApi.java:137
  - api/openapi.yaml:42, documented as /groups/groups/light.json

The earlier audit missed them by grepping the versioned spelling
(`light/groups`); every real caller uses the alias spelling
(`groups/light`).

The plural `%ui %groups` arm is still genuinely dead and stays deleted --
its only references were the per-group `/vN/ui/groups/{ship}/{name}` arm,
which survives, and a timeout test that used the path arbitrarily.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGt4ADrmVsoD8mxunqeZxp
#6188 claims `[%x %v9 %init ~]` and `[%x %v10 %changes ~]` for v10-native
activity, and adds its own `mar/ui/init-9.hoon` and `sur/ui.hoon` `init-9`.
Blob ships after activity, so it takes the next numbers instead of folding
into those arms:

  /v9/init      -> /v10/init       init-9 -> init-10
  /v10/changes  -> /v11/changes    %ui-init-9 -> %ui-init-10
  GroupsInit9   -> GroupsInit10    ChangesV10 -> ChangesV11

Still on `activity:v8:av` because `v10` does not exist until #6188 merges.
That flip is required, not cosmetic: `v9:source:v10:ac` returns `~` for
`%note` and `%notebook`, so a v8-sourced init or changes silently drops
every note unread. Three sites to change on rebase — `sur/ui.hoon`
`init-10`, `groups-ui.hoon` `/v4/activity` in both arms, and
`mar/ui/init-10.hoon`'s enjs call.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGt4ADrmVsoD8mxunqeZxp
…paque-blob-field-custom-metadata

Notes activity (#6188, #6200, #6256) landed, so the blob init/changes arms
stack on top of it and adopt v10-native activity.

  sur/ui.hoon        keep develop's init-9; init-10 = groups-ui:v11 + activity:v10
  groups-ui.hoon     keep /v9/init and /v10/changes; /v10/init and /v11/changes
                     now scry /v6/activity instead of /v4
  mar/ui/init-10     enjs activity:v8 -> activity:v10

The activity flip is required, not cosmetic: v9:source:v10:ac returns ~ for
%note and %notebook, so a v8-sourced init or changes drops every note unread.

  initApi/changesApi hard cutover to /v10/init and /v11/changes, dropping
                     develop's getActivitySupportsNotes() fallback tier
  piers              take develop's rube-*29; the -group-blob archives predate
                     notes activity, and rube applies the desk delta on top
  gateway-status     take develop's; %gateway-status is gone now, superseding
                     the intermediate poke-proxy fix on this branch

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGt4ADrmVsoD8mxunqeZxp

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae68435664

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread desk/mar/ui/init-10.hoon Outdated
arthyn and others added 5 commits August 12, 2026 14:48
init-10:u carries activity:v10:av from /v6/activity, but the mark still
serialized it with activity:v8:enjs. v10 adds %notebook and %note to
$source, so v10 does not nest in v8 — the mark could not compile, and had
it compiled it would have dropped note unreads from the init JSON.

The flip was made while resolving the develop merge but never staged: the
file was not among the conflicted paths, so `git add` of the conflict list
missed it and the merge committed the index. The Hoon suite passed because
run-tests.sh rsyncs the working tree rather than HEAD.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGt4ADrmVsoD8mxunqeZxp
…roups-add-group-level-opaque-blob-field-custom-metadata
The harness boots ~zod, ~ten and ~mug (docker-compose.base.yml serves mug
on 8082 from rube-mug29.tgz) but only committed the branch desk onto the
first two, leaving ~mug on whatever desk its pier archive shipped with.

A mixed-version fleet is harmless until a branch bumps the negotiated
%groups protocol version. This one does (~.groups %2 -> %3, since u-group
%create embeds $group), so ~zod and ~ten classify ~mug as a
certain-mismatch, and negotiate does not defer a poke to a mismatching
gill -- lib/negotiate.hoon:295-302 crashes outright:

    ~|  [%negotiate %poke-to-mismatching-gill gill]
    !!

~mug is the reel/lure provider, so every scenario touching group or channel
setup died while DM-only scenarios passed: 10 failed, 12 passed.

The failure was near-undiagnosable because the crash arrives through a
spider thread whose outputMark is json, and mar/tang.hoon is not in
peru.yaml's pick list -- so %tang -> %json has no cast and the tang never
reaches the caller. Only the ship console carried the hint.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GGt4ADrmVsoD8mxunqeZxp

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f778ccdfae

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const response = await scry<ub.ChangesV11>({
app: 'groups-ui',
path: `/${changesVersion}/changes/${encodedTimestamp}`,
path: `/v11/changes/${encodedTimestamp}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Route iOS cached changes through v11

In the iOS background-cache path, apps/tlon-mobile/ios/Shared/Networking/ChangesAPI.swift still requests /v10/changes (or /v8/changes), so a blob update occurring while the app is backgrounded is omitted from the cached group changes. When that cache is applied, syncCachedChanges advances changesSyncedAt to the cache end; the foreground /v11/changes request then starts after the omitted update, leaving the local blob stale until a later full init. Update the native changes fetcher alongside this JS endpoint so cached iOS windows also include v11 groups.

Useful? React with 👍 / 👎.

…roup-scry-surfaces

groups: delete the dead plural ui/groups scries
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants