NYCN × ICN Execution Tranches
Status: Merge-order + dependency plan, grounded against main at 37ec91e0 (2026-04-14)
Companions: NYCN-Repo-Architecture-Spec.md, NYCN-Implementation-Matrix.md
This document sequences the NYCN integration into concrete tranches. Each tranche is one or two PRs with explicit entry conditions, exit conditions, and dependencies. Agents and humans should read this before opening any NYCN-related branch.
0. Dependency graph (high-level)
Tranche 0: Land open work
├── 0a: Merge PR #1543 (meetings)
└── 0b: PR the notification-digest branch (rebased on #1543)
│
▼
Tranche 1: Program + Milestones + scope-aware digests
│
├──► Tranche 2: Sponsor + BudgetItem
└──► Tranche 3: Session + Registration + Accessibility
│
▼
Tranche 4: SourceRef + ny-coop-net importer
│
▼
Tranche 5: InstitutionalDocument + provenance surfacing
│
▼
Tranche 6: RoleDelegation + Organizer Onboarding
Tranches 2 and 3 parallelize once 1 lands. Tranche 4 can start research/scripting in parallel with 2 and 3 (docs-only / scripts-only), but its code changes inside icn-governance should wait for SourceRef to be reviewable without conflicting with 2/3.
1. Tranche 0 — Land open work
Entry condition: main at 37ec91e0. No other tranche should start until 0 is clear.
0a. Merge PR #1543 (Meeting management)
- Status: OPEN, mergeable, CI green except non-blocking benchmark regression.
- Actions:
- Request human review (one reviewer with meaning-firewall + governance context).
- Address any human review comments.
- If benchmark regression is deemed acceptable, add
perf-regression-oklabel; otherwise profile and mitigate the 18% postcard serialization regression flagged by the bench bot. - Merge via
/push-gated workflow →gh pr merge 1543 --squash --delete-branch.
- Exit condition:
maincontainsmeeting.rs,SledMeetingStore, meeting HTTP endpoints,ActionItem.meeting_id, andMeetingCreated/Started/Endedgateway events.
0b. PR + ship the notification-digest branch
- Status:
feat/notification-digestsexists local+remote, 1 commit ahead of main, no PR yet. - Required rework before opening the PR:
- Rebase on post-#1543 main. Old base (
37ec91e0) is before meetings land. - Unstub
upcoming_meetingsinGovernanceManager::generate_digest. UseSledMeetingStore::list_for_scope(or equivalent) filtered toscheduled_atwithin the next 48h, where scope is derived from the requesting DID's role assignments. - Rename the assignee index to follow the existing
<thing>_by_<scope>convention:action_item_by_assignee:{did}:{domain_id}:{item_id}(notai_idx:assignee:*). Match howSledStructureStore/SledActivityStorename their secondary keys onmain. - Add assignee-index backfill to
SledActionItemStore::open(or equivalent init path). Write a_meta:idx_version:assigneemarker; if missing or below current version, walk all action items once and populate the secondary index. Add a unit test for the backfill on a pre-populated store. - Add
GovernanceActionItemCreatedgateway event — emit fromapps/governance/src/actor.rswhere the decision→action bridge materializes items. This is ~20 LOC and lets the digest architecture (and every future consumer) be event-driven from the start instead of store-polling. Variant fields:{ item_id, domain_id, linked_proposal, assignee }. Follow the existingGovernance<Thing><Verb>naming pattern. - Keep scope partitioning out of scope for this PR — it's a v1.1 concern; PR scope is "correct DID-level digest".
- Add a scenario test: create proposals with
action_items_on_accept, close them, verifyGovernanceActionItemCreatedfires, and that the digest reflects the materialized items plus upcoming meetings.
- Rebase on post-#1543 main. Old base (
- PR body must call out:
- Depends on #1543 (meeting store).
- Notes that
SledActionItemStoreassignee index is being introduced under the<thing>_by_<scope>naming convention — CI must pass on both empty and pre-populated stores. - Introduces
GovernanceActionItemCreatedgateway event (first non-proposal governance-event variant).
- Exit condition: PR open, CI green, review approved, merged.
Tranche 0 risks
- If #1543 does not merge within a week, consider whether to base the digest rework on the #1543 branch directly and merge them as a stack — but prefer clean sequential merging.
- If #1543 hits unexpected review blockers, the digest work still cannot ship without its meeting-store dependency.
2. Tranche 1 — Program + Milestones + scope-aware digest partitioning
Entry condition: Tranche 0 complete.
Scope: Large (Opus-grade design on Program/Milestone; Sonnet-executable rest).
Branch: feat/program-container.
1.1 Add icn-governance::program
- Types:
ProgramId,ProgramKind,ProgramStatus,Program,Milestone,MilestoneId,MilestoneType,MilestoneStatus,CheckRef,InMemoryProgramStore. Program.activity_idas an optional link; do not require it. Program is the first-class container; Activity is kept for lighter cases.
1.2 Add SledProgramStore in apps/governance/src/manager.rs
- Key prefix
program:<id>. - Secondary indexes:
program_idx:parent_entity:<eid>:<pid>,program_idx:status:<status>:<pid>,program_idx:kind:<kind>:<pid>. - Backfill pattern with
_meta:idx_version:program:<name>markers.
1.3 HTTP surface
POST/GET /gov/programsGET /gov/programs/{id}POST /gov/programs/{id}/milestonesPATCH /gov/programs/{id}/milestones/{mid}POST /gov/programs/{id}/closeGET /gov/programs/{id}/dashboard— composes program + milestones + action-item counts by parent + open-decision count
1.4 Gateway events
GovernanceProgramOpened,GovernanceProgramClosed,GovernanceMilestoneCompleted,GovernanceMilestoneBlocked. Follow the existingGovernance<Thing><Verb>naming pattern onGatewayEvent.- Also emit
GovernanceStructureCreated,GovernanceRoleAssigned,GovernanceActivityCreated,GovernanceActivityStatusChangedhere if Tranche 0b has not already added them — these were assumed present in earlier planning but are not onmain.
1.5 Scope-partitioned digest (v1.1 of digest)
- Extend
generate_digest(did, scope: Option<DigestScope>). DigestScope::Structure(sid) | Program(pid) | Entity(eid) | All.- Handler:
GET /gov/digest?scope=structure:nycn-finance.
1.6 MyScope view
GET /gov/me/scopes— enumerate current user'sRoleAssignments grouped by scope, with authority type and time-bounds. Trivial but unblocks UI.
1.7 Scenario tests
- S1 (backbone → full-team transition) with Program/Milestone.
- Milestone transition gated on required checks.
- Scope-partitioned digest returns disjoint results for two scopes the same user is in.
1.8 Exit condition
PR merged; NYCN charter bootstrap can create summit-2026 as a Program { kind: AnnualSummit, parent_entity_id: "nycn-organizers" } with StrategyLocked, VenueLocked, BudgetLocked, PublicLaunchReady, EventReady, ClosureComplete milestones.
3. Tranche 2 — Sponsor + BudgetItem (funding closure)
Entry condition: Tranche 1 merged.
Scope: Medium. Parallelizable with Tranche 3.
Branch: feat/sponsor-budget.
2.1 Add icn-governance::sponsor
Sponsor { id, entity_ref: Option<String>, program_id, relationship_owner_did, status, tier, amount, follow_up_at, benefits_status, source_refs, notes }.SponsorStatus { Prospect, Warm, Asked, Negotiating, Confirmed, Declined, Fulfilled }.- Lifecycle validation: disallow illegal transitions (e.g., Declined → Confirmed is only via a new Sponsor record).
2.2 Add icn-governance::budget_item
BudgetItem { id, program_id, category, amount, direction: Income|Expense|Commitment|Forecast, status, owner_did, linked_party, due_at, source_refs }.- No double-entry bookkeeping here. Budget items are program-level operational planning records; treasury remains in
icn-ledger. If/when a budget item is realized financially, it references a ledger journal entry by ID — but BudgetItem itself does not post ledger entries.
2.3 Sled stores + HTTP
SledSponsorStore,SledBudgetItemStoreinapps/governance/src/manager.rs.- Endpoints per §13 of the architecture spec.
2.4 Sponsor → Budget + Action auto-materialization
- On
Sponsor.status → Confirmed: emitSponsorStatusChanged, auto-create aBudgetItem { direction: Income, status: Forecast, amount: sponsor.amount, linked_party: sponsor.entity_ref }, and auto-create a recognitionActionItemwithparent = InstitutionalParent::Activity(program_activity_id)assigned to the marketing-committee lead role holder. - Use a deterministic materialization pattern (keyed by sponsor ID + event) so retries are idempotent.
2.5 Gateway events
SponsorStatusChanged,BudgetItemCreated,BudgetItemUpdated.
2.6 Scenario tests
- S5 (sponsor pipeline → budget → action).
- Idempotence: re-emitting
SponsorStatusChanged(Confirmed)does not duplicate the budget item or action item.
2.7 Exit condition
PR merged; NYCN can track sponsor prospects and confirmed funders with automatic budget and recognition follow-ups.
4. Tranche 3 — Session + Registration + Accessibility
Entry condition: Tranche 1 merged. Parallelizable with Tranche 2.
Scope: Large.
Branch: feat/session-registration.
3.1 Add icn-governance::session
Session { id, program_id, title, track, format, speaker_dids, status, language_needs, publication_state, representation: RepresentationMetadata, source_refs }.SessionStatus { Idea, Invited, Confirmed, Published, Cancelled }.RepresentationMetadata { sector, geography, language, cooperative_type }— allOption<String>, used for representation dashboards.
3.2 Add icn-governance::registration
Registration { id, program_id, actor_did, ticket_type, payment_state, dietary_needs, childcare_needs, language_needs, accessibility_notes, volunteer_interest, organizer_interest, checkin_state, source_refs }.ChildcareNeed { None, RequestedOnSite, RequestedNearSite, PartialCoverage }.
3.3 Add icn-governance::accessibility
VenueAccessibility { transit_score, hotel_walkability_score, childcare_fit, interpretation_supported, sobriety_friendly, navigation_clarity_score, restroom_inclusion_notes, family_support_notes, environmental_quality_notes }.AccessibilityScore { Poor, Fair, Good, Excellent, Unknown }.- Embed on
Program.metadata.venue_accessibility(serde passthrough) or as a first-class optional field on Program — prefer first-class.
3.4 Sled stores + HTTP
SledSessionStore,SledRegistrationStore.- Endpoints per §13 of architecture spec.
GET /gov/programs/{id}/accessibility-status— composes venue accessibility + aggregated registration accessibility needs into a single status struct.GET /gov/programs/{id}/representation— aggregatesSession.representationfields into buckets.
3.5 Gateway events
SessionPublished,SessionStatusChanged,RegistrationConfirmed,RegistrationCheckedIn.
3.6 Scenario tests
- S4 (registration accessibility feedback).
- Representation bucket aggregation returns stable counts under rapid session status changes.
3.7 Exit condition
PR merged; attendee and content dashboards populate; logistics committee can query accessibility status.
5. Tranche 4 — SourceRef + ny-coop-net importer
Entry condition: Tranches 2 + 3 merged (SourceRef is attached to sponsor/budget/session/registration records from day 1 going forward, but the importer populates historical data).
Scope: Medium Rust + scripts.
Branches: feat/source-ref (docs-touching Rust), chore/nycn-importer (binary/script).
4.1 Add icn-governance::source_ref
SourceSystem { GoogleDoc, GoogleSheet, DriveFile, LegacyRepo, MailingList, NyCoopNetRow, ManualEntry, Other(String) }.ProvenanceType { Linked, Mirrored, Imported, Derived }.SourceRefstruct as specified.- Attach
source_refs: Vec<SourceRef>field (#[serde(default)]) to:Program,Sponsor,BudgetItem,Session,Registration,InstitutionalDocument(when Tranche 5 lands), and any entity/participation records created via import.
4.2 bins/icn-nycn-import (or scripts/nycn-importer/)
- Reads ny-coop-net Postgres (connection string via env).
- Maps tables → canonical ICN objects:
organizations→Entity { type: Cooperative | Community }orParticipation { type: MemberOrg }depending on relationmembers→Participation { type: Member }linked to entitiesevents/summits→ActivityorProgram(summit years as Program, misc events as Activity)sponsors→Sponsorwith status mapped from legacy stateregistrations→Registration
- All imported records carry
SourceRef { source_system: NyCoopNetRow, external_identifier: "<table>:<pk>", provenance_type: Imported }. - Writes via the gateway HTTP API — never directly to sled — so validation is exercised identically to normal create flows.
- Idempotent: re-running must not duplicate. Dedup by (source_system, external_identifier).
4.3 Scenario tests
- Import + re-import does not duplicate.
- Random-sample verification: pick 10 ny-coop-net rows, assert each maps to an ICN record with matching
SourceRef. - Provenance round-trip:
GET /gov/provenance/<id>returns the source ref.
4.4 Exit condition
- NYCN can be bootstrapped on a fresh ICN node by running the importer. Historical sponsor pipeline, summit attendees, and member orgs are present with provenance links to ny-coop-net.
6. Tranche 5 — InstitutionalDocument + cross-object provenance
Entry condition: Tranche 4 merged.
Scope: Large. Design-Opus, implementation-Sonnet.
Branch: feat/institutional-documents.
6.1 Add icn-governance::document
- Content-addressed (
id = blake3(body)). DocumentType { MeetingAgenda, MeetingMinutes, CommitteeReport, Policy, Evaluation, General }.- Linked-to-scope via
InstitutionalParentOR linked to a specific meeting/proposal instance. - Version lineage via
parent_doc. attachments: Vec<AttachmentRef>(each attachment is content-addressed separately).
6.2 Gossip integration
- Topic:
governance:{domain_id}:documents. - Published as gossip entries; content-addressed so replication is automatic via existing protocol.
- Sled indexes:
doc:<domain>:<doc_id>,doc_idx:<domain>:<type>:<timestamp>,doc_link:<scope_tag>:<scope_id>:<doc_id>.
6.3 HTTP surface
POST /gov/documents,GET /gov/documents/{id}, version-history list, search.
6.4 Meeting notes linkage
Meeting.notes_doc: Option<DocumentId>(add field now, defaulting toNone, on the meeting model so post-meeting summaries can attach the minutes document).
6.5 Provenance surfacing
GET /gov/history/{ref}— walks the provenance chain. Given anActionItem, returns: action → meeting → agenda item → decision (proposal) → accepted tally → originating documents.GET /gov/provenance/{ref}— raw provenance edges only.
6.6 Scenario tests
- S7 (provenance chain).
- Cross-node gossip: document created on node A is retrievable and hash-verifiable on node B.
- Version lineage: v1 → v2 → v3 queryable by parent_doc chain.
6.7 Exit condition
- A new organizer can walk from their action item inbox back through meetings, decisions, and supporting documents to understand the full lineage of any obligation.
7. Tranche 6 — RoleDelegation + Organizer Onboarding
Entry condition: Tranche 5 merged.
Scope: Medium.
Branch: feat/role-delegation-onboarding.
7.1 Add icn-governance::role_delegation
- Distinct from vote-delegation (
icn-governance::delegation). RoleDelegation { id, grantor_did, grantee_did, scope_ref, capabilities, starts_at, ends_at, reason, revoked_at }.- Sled store + HTTP (
POST /gov/role-delegations,POST /gov/role-delegations/{id}/revoke,GET /gov/me/delegations). - Active-scope resolution in the gateway includes active delegations within time window.
7.2 Add icn-governance::onboarding
OnboardingPipelineEntry { id, actor_did, pipeline_type (interest | orientation | role-assigned | first-task | continuity), status, created_at, promoted_at, structure_interest: Option<StructureId>, notes }.- HTTP:
POST /gov/onboarding,PATCH /gov/onboarding/{id}/advance,GET /gov/onboarding. - View:
OrganizerOnboardingView— pipeline state + next actions per entry.
7.3 Scenario tests
- Delegation grant → grantee acts in scope → revocation → grantee loses capability.
- Onboarding pipeline traversal: interest → role-assigned → first-task completion.
- S6 (cycle closure + handoff): closed program's role assignments do not block new organizers onboarding to the next cycle's program.
7.4 Exit condition
- Organizers can be onboarded and delegated to with full auditability; annual cycle handoff leaves clean state.
8. Docs and charter work (parallel / continuous)
These are not code tranches but must progress alongside:
8.1 After Tranche 0 lands
- Update
docs/strategy/NYCN-Implementation-Plan.md: Phase 3 (meetings) → ✅, Phase 4 (digests) → ✅ for v1; add v1.1 (scope partitioning) as a note. - Update
docs/INDEX.mdto reference this architecture spec and matrix docs.
8.2 After Tranche 1 lands
- Revise
docs/strategy/NYCN-Institutional-Design.md:- Replace the committee-as-Community entity-tree diagram with the Structure-based topology.
- Add Program wrapping.
- Note that the older entity tree is superseded.
- Add
docs/strategy/NYCN-Bootstrap-Runbook.md(does not currently exist despite prior-session claim).
8.3 After Tranche 4 lands
- Add
docs/strategy/NYCN-Schema-Mapping.mddocumenting the ny-coop-net → ICN mapping actually implemented by the importer.
8.4 NYCN Charter YAML
docs/strategy/NYCN-Charter-Draft.yamlexists. When Tranche 1 lands, update the charter YAML to declare programs and milestones if the CCL schema supports them. If not, open a CCL extension proposal.
9. Rollback and risk mitigation
9.1 Tranche rollback rule
Each tranche is independently revertable via gh pr revert. Specifically:
- Reverting Tranche 0 restores digest-less, meeting-less state — no data loss.
- Reverting Tranche 1 requires first reverting all later tranches that reference Program. Phase gate: if Tranche 1 has problems, no later tranche should have merged.
- Tranches 2 and 3 are independently revertable from each other.
- Tranche 4 importer bugs do not require code revert —
SourceRef-tagged records can be bulk-deleted by source-system filter. - Tranche 5 documents are content-addressed; reverting the code still leaves gossiped documents on peer nodes. Plan a clean-shutdown migration if this becomes a live concern.
9.2 Load-bearing risks
| Risk | Mitigation |
|---|---|
| Program/Activity overlap confusion | Clear docstring separation; both primitives kept but linter/docs explain when to use which |
| Authority ambiguity (backbone vs full group) | Model via RoleAssignment.authority_type + PolicyOracle rules, not hardcoded logic |
| Endpoint sprawl | No sponsor/budget/session/registration endpoints before Tranche 1 lands |
| Spreadsheet-shaped architecture | All imported data carries SourceRef; canonical shape is ICN-native, not sheet-derived |
| Meaning firewall breach | No NYCN-specific strings in kernel crates; only in seed data and test fixtures |
| Digest staleness / index drift | Every new secondary index includes backfill-on-init with version marker |
| Migration double-import | Idempotent import keyed on (source_system, external_identifier) |
10. Agent assignments
| Tranche | Suggested agent(s) | Reason |
|---|---|---|
| 0a (merge #1543) | human review + icn-rust-core for any review fixes |
meaning-firewall sensitive |
| 0b (digest rework) | icn-rust-core |
index backfill + store composition |
| 1 (Program + Milestones) | icn-governance-advisor design + icn-rust-core impl |
governance state machine correctness |
| 2 (Sponsor + Budget) | icn-governance-advisor + icn-economics-advisor on the budget/ledger boundary |
ledger boundary risk |
| 3 (Session + Registration + Accessibility) | icn-rust-core with icn-governance-advisor review |
operational domains |
| 4 (SourceRef + importer) | icn-rust-core + human operator for the importer binary |
touches external DB |
| 5 (Documents + gossip) | icn-gossip-net + icn-rust-core + icn-governance-advisor |
gossip integration |
| 6 (RoleDelegation + Onboarding) | icn-identity-iam-advisor + icn-governance-advisor |
IAM + governance crossover |
Every tranche also gets one pass by icn-invariants-guardian before merge.
11. Success criteria (the decisive test)
Can a new organizer enter the system mid-cycle, switch into the right scope, see the summit's current phase, understand what was decided, know what is blocked, receive their obligations, trace why they exist, and continue the work without needing private oral history?
| Tranche | What this enables toward the decisive test |
|---|---|
| 0 | Meetings recorded; overdue work surfaces in digest |
| 1 | New organizer can see summit's current phase (milestones) + their scope |
| 2 | New organizer can see funding state and follow-ups without asking |
| 3 | New organizer can see content + attendees + accessibility status |
| 4 | Historical context from ny-coop-net is present with provenance |
| 5 | New organizer can walk back from any obligation to the document that originated it |
| 6 | New organizer can be onboarded with delegated scope without oral handoff |
When Tranche 6 is merged and a migration has happened, the decisive test returns "yes" for the first time.
12. Start line
- Immediate next action: Push
feat/notification-digestsrework is blocked until #1543 is reviewed + merged. So the very next action is: request human review on PR #1543. - Second action: once #1543 is merged, rebase
feat/notification-digestson main, apply the digest-rework list in §2.0b, push, open PR. - Third action: design review for Tranche 1 (
Programmodule) withicn-governance-advisorandicn-invariants-guardianon the proposed module shape before opening a branch.
Do not start Tranche 2+ branches until Tranche 1 is under review.