Goal Instance Identity and Orphan Recovery (v0)¶
- RFC status: Accepted
- Supersedes / closes: none
- Delivery maturity: Partial source-session lifetime and binding-owner implementation; quota settlement qualified in #5389; activation, orphan recovery and product acceptance remain held
- Authors / owners: LoopX contributors
- Created: 2026-09-23
- Last normative revision: 2026-10-02
- Implementation baseline:
23edcb19c70394480e3a9ebe8a960f5a320c5342 - Related contracts: Issue #4801, orphan fence slice #4808
- Language mirror: Chinese semantic mirror
Document map and maintenance contract¶
Sections 1-10 are the durable design and acceptance contract. Section 11 is the normative delivery plan. Section 12 contains unresolved decisions; recommendations there are not approval. Appendices are non-normative evidence and decision history.
The English and Chinese documents are semantic mirrors. Any normative change must update both in the same pull request.
1. Decision summary¶
This RFC makes five linked decisions:
goal_idremains the human-readable alias. A project-registry-owned, immutablegoal_instance_ididentifies one Goal lifetime.- Every durable host, session, channel, automation, Turn, and quota binding carries the exact pair. The source project registry, not a global projection or a binding store, authorizes execution.
- Instance identity needs an incompatible activation format plus commit-time fencing. The shipped M0 codec is not an identity-enforcement protocol; rejecting one old decoder alone does not exclude every old effect path.
- Orphan recovery is one preview-first, journaled lifecycle operation with
explicit per-candidate
adopt,migrate,archive, ordeletedispositions. It never guesses or merges. - Activation is explicit and registry-wide. Within an activated registry, unstamped bindings are read-only. Non-activated projects retain their legacy behavior and have no ABA guarantee; a fleet-wide default change is separate.
The existing state-file locations, host-specific runtime directory layouts, provider revision domains, leases, and global registry role do not become new Goal authorities. This RFC does not approve automatic cleanup of every external provider, automatic candidate selection, or direct mutation of orphan state by ordinary bootstrap.
Post-merge recovery integration (2026-10-16)¶
#6396 retires ordinary legacy source writers in the 2.0 tree. It does not mechanically exclude installed old binaries, global routes or warm executors, and does not lift this RFC's activation hold. Continue #5206's remaining binding-owner inventory, commit fences and external-effect drain; reuse delivered codec/source-session/quota owners instead of restarting them.
Coordinate only the exercised local profile with complete-recovery M2/M3 and R5. Goal lifetime, provider incarnation, execution/session generation and lease epoch remain distinct. Freeze replacement continuity versus a new lifetime through the Goal owner, then prove that old A cannot write, deliver, debit or settle same-alias B after recovery. M4 orphan resolution keeps its preview/backup/journal/retry boundary; M5 adds the real product/peer result and original-route return only after the profile's M2/M3 fences pass. Component fixtures may precede activation but cannot certify it. Ordinary supported creation/collaboration need not wait for all service profiles; this checkpoint grants no activation or recovery authority.
2. Problem and motivation¶
Today one goal_id serves as both a display name and a durable identity. A
Goal can disappear from the project registry while active-state files,
sessions, host bindings, Goal Channels, and heartbeat automations survive.
Creating a new Goal with the same goal_id can make those old records appear
current. This is an ABA failure:
- Goal A named
releasecreates durable attachments. - Goal A is removed, but some attachments remain.
- Goal B is created with the same name.
- An attachment that only stores
releasecannot distinguish A from B.
The fence shipped by #4808 correctly blocks guided bootstrap when a registry entry is absent but a project state candidate remains. It does not distinguish Goal lifetimes, resolve the orphan, or invalidate attachments after delete-and-recreate.
No individual host or session store can solve this locally. If a host invents identity, it becomes a second Goal authority. If deletion synchronously knows every provider, it becomes a cross-owner distributed cleanup transaction. LoopX needs one source-owned lifetime identity, with admission and commit fenced against retirement. An identity match is necessary, not an authorization grant.
Invariants¶
- The source project registry is the only authority that creates or resolves a Goal instance.
- A new Goal after terminal removal receives a fresh random instance ID,
including when its
goal_id, path, objective, or configuration is reused. - Updating configuration, reconnecting a provider, renewing a lease, opening a Turn, or rewriting a binding never rotates the instance ID.
- A stale, missing, malformed, or legacy binding cannot authorize execution for an instance-aware Goal.
- A stale global projection may route a lookup but cannot authorize a write.
- Before source safety is complete, a resolution journal blocks ordinary activation/mutation of that Goal, including after registry publication. Exact recovery remains available; completed-source projection retry is separate.
- Resolution leaves exactly one selected writable state authority or leaves the Goal absent with no writable candidate.
- Destructive resolution requires a verified targeted backup, exact plan digest, explicit Goal confirmation, and post-write readback.
- Crash retry reuses prepared identity and owner idempotency keys; every plan, including archive/delete-only plans, journals before its first mutation.
- A released old writer cannot silently open or rewrite an instance-aware project registry.
3. Scope and non-goals¶
In scope¶
- Project-registry Goal lifetime identity and exact binding comparison.
- A registry-wide compatibility boundary for old readers and writers.
- Instance propagation through first-party durable binding owners.
- Final checks at activation, resume, delivery, state mutation, and quota authorization.
- Preview, backup, apply, resume, and rollback for project-local orphan state.
- Diagnostics for orphan state, legacy identity, stale bindings, stale global projections, incomplete journals, and manual cleanup.
- A staged migration that separates observation from enforcement.
Non-goals¶
- Changing the human-facing
goal_idor placing instance IDs in state paths. - Migrating low-level Codex, Pi, OpenCode, or other host runtime directories.
- Merging multiple active-state candidates.
- Treating timestamps, paths, content equality, global projections, or host records as identity evidence.
- Making physical deletion from an external provider the safety boundary.
- Introducing a public generic binding-provider framework.
- Changing Claim, Lease, authority revision, Turn, or provider idempotency semantics beyond binding them to a Goal instance.
- Allowing an old binary to downgrade or edit a strict registry.
Roadmap placement and contract cooperation¶
The overall roadmap measures accepted outcomes, human attention and recovery. This RFC contributes a bounded R5 lifetime fence that R2/R3 can consume; it does not complete R1–R7 or promote a provider.
| Existing owner / contract | Contribution and boundary |
|---|---|
| TS convergence, R5 | Put match, admission, retirement and recovery transitions in the typed kernel; keep Python codec, filesystem and host adapters. Do not add a second Python policy engine or per-field RPCs. |
| Shared authority, D1–D3 / R6 | Bind the existing head, operation receipts and authorization projection to lifetime. Provider incarnation, revision, lease epoch and Goal instance remain different domains; no default cutover or new store. |
| Shared alignment, R4 | Identity is not intent, permission or work revision. Preserve intent_basis source-facts semantics; amendments remain with their existing owner. |
| Semantic handoff, collaboration, R2/R3 | Bind request, adoption, result and original-conversation return to the instance. Late A results cannot settle B; transport success and registration do not prove acceptance. |
| Single-owner daemon | Reuse profile quiescence and actual executor drain; a PID, package version or stopped heartbeat is not proof that old writers cannot return. Daemon identity grants no Goal authority. |
#4808 owns the shipped guided fence; #4912 proposes diagnosis; #4915 proposes host-neutral paths. Reuse those owners without treating open PRs as shipped. Canonical Todos own execution status; this document owns acceptance and dependency boundaries.
4. Current-system contract¶
At the named main baseline, #4917
has shipped control_plane/projects/registry_codec.py, format-preserving
transactions and a production I/O census. It reads legacy objects and strict
loopx_project_registry_envelope_v1 arrays and accepts writer protocol
goal_instance_v1. It does not mint, compare or enforce Goal instances.
Therefore a codec-capable but identity-unaware binary is part of the old-writer
population; the original object-root-only argument is insufficient.
The census is now computed from current tracked sources by the existing semantic
premerge smoke. Only reviewed direct-I/O classifications are versioned in
loopx/semantics/project_registry_io_classifications_v1.json; call coordinates
and codec sites are derived. This preserves the M0 codec-boundary check while
removing snapshot churn. Unclassified direct I/O and stale classifications still
fail; see source validation.
The project registry owns configuration and source identity; the global registry
is a host-local route projection. Canonical coordination already has typed TS
transaction owners, independent provider incarnations and receipts.
coordination/authority_source.ts and authority_source_capture.py witness
registry facts, explicitly without a transaction spanning registry and provider.
Their digest recheck must not be advertised as retirement serialization.
orphaned_goal_state.py discovers current/legacy candidates and fences guided
bootstrap. state_backup.py owns backup/verification. Bootstrap updates, Goal
deletion, sessions, channels and quota remain distinct callers. Their current
alias/revision bindings do not prove lifetime continuity. No instance activation
or orphan mutation command described below has shipped at this baseline.
5. Proposed architecture¶
5.1 Ownership and authority¶
Placement: existing Goal/project lifecycle in the built-in control plane; no new public capability id, provider id or extension distribution. File, SQLite and PostgreSQL keep their existing provider contracts and qualification gates.
The source authority alone allocates and publishes goal_instance_id. A prepared
resolution may reserve one ID durably; it has no execution authority until the
lifecycle commit. The global registry only copies it. Pure match and lifecycle
transitions belong in the nearest typed TS Goal boundary, consuming source facts
through one coarse transaction. Python adapts CLI, codec, filesystem, backup and
host effects, and executes typed plans without duplicating transition rules.
The project registry remains the local source of identity. For a future R6 service profile, its versioned authorization projection and instance are admitted by the authenticated authority service. Remote workers do not open a laptop's path, allocate from a local clone or use a global projection as authority. Until that path is qualified, instance enforcement is explicitly local-profile only.
No binding, path, timestamp or content hash may infer identity. Existing grant, claim, lease, quota, stop and intent checks still apply after an exact match.
5.2 Goal identity model¶
goal_instance_id is opaque, immutable, public-safe, and not a credential. Its
canonical form is ginst_ followed by 32 lowercase hexadecimal characters,
providing 128 random bits.
The typed contract distinguishes GoalRef { goal_id, goal_instance_id } from
LegacyGoalRef { goal_id }. BindingMatch is an exhaustive result with
current, legacy_read_only, missing_goal_instance_id,
goal_instance_mismatch, goal_not_registered, resolution_in_progress, and
invalid variants. These are kernel decisions; generated/decoded Python
representations are wire adapters, not a second matching implementation.
An instance-aware Goal record contains:
{
"id": "release-2026",
"goal_instance_id": "ginst_6ff38d6d143d4b72a6ff894b95f067c1",
"status": "active",
"repo": "/project",
"state_file": ".codex/goals/release-2026/ACTIVE_GOAL_STATE.md"
}
The absolute path above is illustrative, not public evidence. Stored paths continue to follow existing privacy and portability rules.
Every durable attachment stores:
The matching matrix is exhaustive:
| Registry state | Binding state | Read-only inspection | Execution or mutation |
|---|---|---|---|
| Exact instance | Exact same instance | allow | allow under commit fence and existing grants |
| Exact instance | Missing instance | allow with finding | reject |
| Exact instance | Different instance | allow with finding | reject |
| Legacy Goal | Legacy binding | allow with finding | reject in activated registry |
| Legacy Goal | Instance binding | allow with finding | reject |
| Goal absent | Any binding | allow as historical | reject |
| Resolution source safety incomplete | Any binding | allow with finding | reject ordinary execution; exact recovery only |
| Invalid record | Any | fail closed | reject |
Observation and non-activated projects retain legacy execution without an ABA
guarantee. An activated project has no legacy_compatible execution branch.
The exact-match row also requires active lifecycle state and existing grants.
5.3 Identity independence¶
The following revision domains remain independent:
| Event or identity | Rotates goal_instance_id? |
Reason |
|---|---|---|
| Fresh creation after terminal retirement | yes | Starts a new Goal lifetime |
Orphan adopt or migrate that creates a Goal |
yes | Establishes a new authority |
| Forced bootstrap or configuration update | no | Changes configuration within one lifetime |
| Provider reconnect or provider revision | no | Changes an attachment or backend generation |
| Authority revision or migration receipt | no | Versions authority state, not Goal existence |
| Claim or Lease epoch | no | Coordinates work inside one lifetime |
| New Turn or session | no | Creates execution lineage within one lifetime |
| Binding revision | no | Versions one owner's attachment record |
| Global registry synchronization | no | Copies a source-owned identity |
| Rollback to orphaned bytes | never restores one | Restored bytes have no execution authority |
--force cannot replace an instance ID. Imports into a new project authority
mint a new identity rather than copying an identity owned by another registry.
Restoring an authority into a replacement deployment follows the existing
incarnation/restore contract; copying a registry backup must not resurrect stale
writers or create a second writable authority.
5.4 Activation format and mixed-writer gate¶
Identity activates per project registry, not per Goal. Reuse the M0 codec and
I/O census; do not build another registry loader. M0's v1 format/protocol remains
codec-only. Proposed activation uses an incompatible header schema
loopx_project_registry_envelope_v2 and minimum_writer_protocol: goal_instance_v2
with the existing two-element [header, payload] shape and payload digest.
The final identifiers require the M2 compatibility matrix before release.
Changing only the minimum writer string is insufficient: M0 readers can decode an unknown writer protocol, and a state mutation may never rewrite the registry. The new schema must reject identity-unaware read-to-effect paths as well as registry writes. Old global-object routes and already-loaded processes need separate exclusion; no header can retrospectively constrain cached code.
loopx activate-goal-instance-identity --project . --format json
loopx activate-goal-instance-identity --project . --execute \
--plan-revision sha256:31ab... --confirm-registry-path .loopx/registry.json
Activation must:
- Stop new admissions and durably quiesce all Goals in the registry through existing lifecycle/host owners; drain or explicitly reconcile admitted effects. Inventory direct CLI, attached/managed hosts, global routes, automation and provider writers. A disconnected or unaccounted writer blocks activation.
- Verify a targeted backup, host protocol support and the exact preview digest. A capability advertisement is evidence to inspect, not write authority.
- Prepare a durable activation journal and one ID per current Goal, then publish the incompatible envelope atomically under the registry transaction. The same IDs survive retry; no ID is minted in ordinary M0/M1 execution.
- Bind owner state through explicit reviewed migration/reconnection. Missing stamps never inherit authority by alias. Reopen admission only after required commit fences and readbacks are installed; pending projection delivery alone must not authorize or duplicate effects.
Before publication, abort leaves the legacy object. After publication, only forward repair is legal; never unwrap for an old writer. Fresh projects may opt into the complete profile. Installation or codec availability does not activate it. Fleet-wide legacy shutdown is a separate release decision.
Test all compatibility classes: object-only packages, codec-only M0/M1 packages, and enforcement packages; test direct source and global routes, warm processes, state writes, resume, quota and delivery. Require unchanged business state and receipts on rejection, not only unchanged registry bytes. Local drain plus unsupported source decoding must be proved for the supported package range; where an old route bypasses both, activation remains unavailable until an existing owner can mechanically exclude it. Same-principal deliberate filesystem tampering is outside this mixed-version safety model.
5.5 Binding owners and commit fencing¶
| Existing owner | Instance-bound state and decisive boundary |
|---|---|
| Registry / attached host / Chat | Session/thread and activation binding; route selection, resume and executor admission |
| Turn / scheduler / quota | Work selection, lineage, settlement, debit/void and replay identity |
| Todo / coordination / lease | Head/provider binding, work mutation, claim/renew/reclaim and receipts |
| Handoff / inbox / outbox | Request, receiver adoption, accepted artifact, result delivery and historical readback |
| Goal Channel / Lark / heartbeat | Connection, delayed inbound/outbound event and wake admission |
| Pi / OpenCode / other first-party hosts | Host action, execution binding and quota/state writeback |
Maintain one private exhaustive owner inventory with stable locator, revision, content digest, observed typed reference and cleanup support. Sorted inventory and owner revisions enter plans. Reuse the M0 I/O census for registry access; binding/effect coverage is a distinct inventory, not proof from the writer count.
Resolve route → validate source/authorized projection → compare exact reference and lifecycle → check existing permissions → admit the operation → commit under the owning fence. A last-moment read alone leaves a check/use race: A passes the check, deletion/recreation publishes B, then A writes the reused path or spends quota. The contract requires an explicit linearization boundary:
- Locally, lifecycle retirement and each short protected commit share the same instance admission guard with documented lock order. Do not hold a registry lock across a model call, network request or user validation.
- In canonical transactions, propagate the original source witness and instance to the TS owner; check the current instance at the commit boundary under the lifecycle guard. Existing CAS and lease fences remain required. A witness hash or Goal ID alone is not this guard.
- Retirement closes admission first, drains admitted commits and reconciles pending external effects in their existing journals, then publishes retirement. Uncertain external effects prevent declaring retirement/reuse complete. An external provider without fencing is not retroactively cancellable.
- A shared service must serialize lifetime and mutation admission server-side, with tenant/actor checks and restore incarnation. A local file lock does not qualify R6; no cross-ledger atomicity is implied.
Scope operation/replay keys, canonical state selection and late results to the instance even when physical paths retain the alias. Old receipts remain readable as A's history, but cannot grant B a lease, satisfy B's acceptance or debit B. Do not silently relabel historical receipts. Selected provider state must either be newly initialized for B or explicitly imported through its reviewed lifecycle. A stale writer that pauses after validation and resumes after recreation must produce no B-side write, delivery, debit or ownership change.
5.6 Orphan resolution command¶
The command is a dry run unless --execute is present. Every discovered
candidate requires one explicit disposition:
loopx resolve-orphaned-goal-state \
--project . \
--goal-id release-2026 \
--disposition .codex/goals/release-2026/ACTIVE_GOAL_STATE.md=adopt \
--disposition .claude/goals/release-2026/ACTIVE_GOAL_STATE.md=archive \
--objective "Ship the release" \
--domain engineering \
--role owner \
--format json
adoptretains selected state content at its canonical location under a fresh Goal instance; it does not revive former claims, grants or execution bindings.migratemoves one selected candidate through the canonical state-path helper, under the same human Goal ID, then registers a fresh instance.archivemoves a candidate outside every active Goal root.deleteremoves a candidate only after targeted backup verification.
At most one candidate may be adopt or migrate. All other candidates must be
archive or delete. An archive/delete-only plan leaves the Goal absent.
There is no implicit winner, content merge, arbitrary destination path, or
renamed target Goal. The first resolution profile is project-local legacy file
state. If a candidate routes to promoted canonical/provider state, fail closed
with its existing authority migration/import route; moving Markdown cannot adopt
a File/SQLite/PostgreSQL head. Any later provider support must preserve source
selection, incarnation, historical receipts and D1–D3 gates.
Preview validates before computing its digest:
- the project and registry resolve inside their declared authority boundary;
- each candidate is a readable regular file inside an allowed Goal root;
- no candidate or parent is a symlink;
- resolved paths do not escape, alias, or duplicate one another;
- the requested Goal is absent from the project registry;
- no other resolution exists for the Goal;
- every candidate tree digest and binding-owner revision is stable;
- the migration destination is selected by the canonical path helper;
- the Goal creation specification is complete when required.
Execution must bind the preview and human intent:
loopx resolve-orphaned-goal-state \
--project . \
--goal-id release-2026 \
--disposition .codex/goals/release-2026/ACTIVE_GOAL_STATE.md=adopt \
--disposition .claude/goals/release-2026/ACTIVE_GOAL_STATE.md=archive \
--objective "Ship the release" \
--domain engineering \
--role owner \
--execute \
--plan-revision sha256:82e3... \
--confirm-goal-id release-2026
5.7 Resolution transaction and journal¶
Use a typed lifecycle with prepared, applying, source_committed,
complete, rollback_prepared, and rolled_back states. The journal lives under
.loopx/lifecycle/orphaned-goal-state/<goal-id>/<plan-revision>.json; it is
recovery metadata, not another registry. TS owns transitions and allowed effects;
existing Python/host adapters execute them and return observations.
- Acquire lifecycle/registry/affected-owner guards in documented order. Exact completed replay returns its historical receipt; it does not reactivate a Goal.
- Before preparation, rebuild and compare the full registry, candidate-tree, inventory, creation-specification and plan digests; mismatch has no effects.
- Verify targeted backup, then durably prepare every plan before any move or deletion, including archive/delete-only plans. Reserve a new ID only if creating a Goal; retain candidate preimages, planned postimages and owner operation keys.
- Apply each disposition under its guard. Persist intent before the effect and reconcile actual pre/post state after crash-before-ack. Neither state matching requires replaying a deletion; neither matching state permits guessing.
- Publish the new Goal only once selected content is canonical and competitors are inactive. Resume a prepared plan from its recorded phase; do not demand the original preview's pre-mutation digests after successful partial work.
- Obtain required local invalidation receipts, verify the source and safety fences, and commit the lifecycle. Complete registry-wide activation first when other legacy Goals exist. Other Goals are outside the resolution guard's scope.
- Deliver the global projection and operator result through existing retry
owners. Projection failure is typed
pending_sync, not a second authority or permission to repeat the source commit.
Before source safety is established, the journal blocks ordinary activation and mutation for this Goal, even if its record is published. Only an exact resolution/resume operation may perform its declared recovery effects. After source commit plus required invalidation, projection lag may remain visible without holding unrelated Goals or source-authorized local work indefinitely. Every event-driven wake is advisory; source checks still decide admission.
The receipt records plan/resolution/terminal-journal digests; candidate pre/post digests; exact source Goal reference; backup manifest verification; each owner observation, revision, operation and readback; local removal receipts; and typed projection/manual-cleanup findings. Exclude raw private content. Recovery must remain available while new lifecycle admissions are disabled.
5.8 Logical revocation and legacy cleanup¶
Exact instance mismatch plus the commit fence is the safety boundary; physical external deletion is hygiene. Typed cleanup selectors distinguish:
instance: exact retired(goal_id, goal_instance_id);legacy_observation: exact owner locator + observed revision + content digest, admitted only while the lifecycle guard proves the Goal absent/inactive.
A legacy orphan has no retired instance to invent. If its owner supports exact
conditional removal, revalidate the observation under the owner guard and remove
only that record. If the revision changed, stop/replan. If the provider cannot
prove exact removal, retain the now-inert record with manual_cleanup_required.
Never use Goal-ID-only bulk cleanup or stamp a legacy binding to make it deletable.
Required local logical invalidation receipts gate source completion; optional physical cleanup and external manual cleanup have separate retry state. A new binding created by a different lifetime cannot match the legacy selector. Test legacy and stamped orphans independently, including a revision race.
5.9 Rollback¶
Rollback is also preview-first and digest-bound:
loopx rollback-orphaned-goal-resolution \
--project . \
--goal-id release-2026 \
--resolution-id ores_... \
--format json
loopx rollback-orphaned-goal-resolution \
--project . \
--goal-id release-2026 \
--resolution-id ores_... \
--execute \
--plan-revision sha256:... \
--confirm-goal-id release-2026
Rollback may restore verified bytes only to an orphaned, fenced state. It
never restores a retired Goal instance ID or binding authority. It is rejected
after any post-resolution Goal write, Todo mutation, quota spend, session or
channel creation, automation installation, or binding revision. Rollback itself
journals before its first mutation and uses the same retirement/commit guard,
so checking eligibility cannot race a new business write. A successful
rollback removes the newly created Goal through the lifecycle owner, restores
candidate bytes, verifies their digests, and makes diagnosis unhealthy with
orphaned_goal_state.
If rollback is no longer legal, recovery is a forward operation: finish the journal, retire the new Goal through normal lifecycle, and create another resolution plan.
5.10 Bounded refactor and delivery owners¶
| Existing boundary | Change and deletion requirement |
|---|---|
TS Goal lifecycle + coordination/authority_source.ts |
One typed reference/match/admission/recovery rule set; extend whole transactions, remove duplicate host decisions. The witness is evidence, not a cross-store lock. |
control_plane/projects/registry_codec.py |
Extend shipped codec/protocol checks and existing transaction; retain v1 compatibility without granting v2 enforcement. |
bootstrap.py / Goal deletion |
Use the lifecycle transaction; preserve identity on config updates and fence retirement/recreation. |
orphaned_goal_state.py / state_backup.py |
Keep discovery and backup effects; a thin resolution adapter drives the typed plan instead of a new Python state machine. |
| Existing binding / delivery owners | Persist exact refs and commit fences; retain owner-local journals and idempotency instead of a central provider cleanup framework. |
| Diagnosis / CLI / Chat / frontend / Lark | Render the same lifecycle result and recovery action, with audience filtering; no second detector or independent repair state. |
Characterize ordinary bootstrap/update/deletion and default-off paths before refactoring. Ship a whole local retirement/recreation path with actual consumers; do not add unused Goal abstractions, per-field RPCs or a new scheduler. Larger cross-host and provider-import work stays with R6/D1–D3, not a prerequisite for the local legacy-file recovery outcome.
6. Alternatives and design choices¶
Generation counter¶
A counter under goal_id needs a permanent tombstone after deletion or a
second global allocator. Random source-registry identity avoids both and does
not require ordered IDs.
Instance ID in directory paths¶
Instance-segment paths separate files but do not identify sessions, channels, automations, or quota calls. They also force every human-facing path consumer through a broad migration. Record-level identity closes the reported ABA failure without that unrelated layout change.
Delete every binding during Goal deletion¶
Cross-provider deletion is not atomic and would make Goal lifecycle know every storage implementation. Exact matching makes stale records inert. Owner-local cleanup then improves hygiene without becoming authority.
Add a capability field to the current registry object¶
Released old loaders accept unknown object fields and schema values. They could silently rewrite an instance-aware registry. This fails the mixed-writer invariant.
A new registry path with a redirect object¶
Released old binaries could ignore the redirect, continue using the old path, and create split authority. Keeping one path with an incompatible root shape causes a deterministic decode failure instead.
Automatic candidate selection or merge¶
Path preference, modification time, and content equality do not prove authority. Multiple candidates may represent divergent histories. The operator must classify every candidate.
One module for discovery and mutation¶
Combining the small read-side fence with backup, journaling, cleanup, rollback, and registry publication would mix stable detection with a large transaction. The sibling resolution owner preserves a deep plan/apply interface without making the fence module a lifecycle service.
7. Safety, privacy, and compatibility¶
Identity and digests are public-safe metadata, not credentials. Backups, candidate contents, session payloads, local absolute paths, provider handles, and raw state remain private runtime data and must not enter public receipts, logs, fixtures, or documentation.
The format gate and effect-owner fences jointly exclude supported mixed writers. Activation requires source/global/warm-process negative tests for object-only, codec-only and enforcement binaries. Test helpers use isolated synthetic state; never probe a live Goal to demonstrate rejection.
Non-activated legacy projects retain existing behavior. Activated projects allow legacy inspection, backup and migration previews but reject unstamped execution. An observation finding cannot advertise ABA protection. Protocol names must represent the full enforced contract, not merely a decoder's availability.
An activated registry cannot be downgraded. Unsupported source schema/protocol must fail before a business effect, including effects that do not edit registry bytes. Recovery uses a current compatible release and never unwraps the payload.
Within the activated profile, source unavailability, digest mismatch, malformed envelope, missing instance, incomplete source safety or owner-inventory ambiguity fails closed. Global projection unavailability may delay visibility but cannot grant execution.
8. Migration and rollback¶
Migration has two scopes:
- Fresh opted-in project. Create the activation envelope and mint the first Goal in its committed registry transaction.
- Existing project. Preview registry-wide activation, quiesce every Goal, back up the registry and local bindings, stamp every current Goal with a new instance, atomically write the strict envelope, and require explicit reconnection of all attachments.
No existing attachment is automatically blessed. There is no evidence that an unstamped record belongs to the current lifetime rather than a prior same-name Goal.
The activation cutover point is the verified incompatible-envelope write; admission remains closed until required owner fences/readbacks complete:
- Before it, abort leaves the legacy object authoritative.
- After it, old binaries and unstamped bindings are unsupported and fail closed.
- If later steps fail, current code resumes the activation journal.
- Returning to legacy execution is not rollback.
Orphan archive/delete may run without identity activation because it creates no Goal. Orphan adopt/migrate requires an already activated registry, a fresh activated registry, or a journaled registry-wide activation admitted by the same plan.
Before resolution preparation, abort changes nothing. After preparation, retry the same plan. The separate rollback command is legal only before any post-resolution business write and restores orphaned, non-executable bytes.
9. Validation and acceptance¶
| Claim | Test or evidence | Required result | Boundary / exclusions |
|---|---|---|---|
| Same-name recreation is fenced | Delete A, preserve every attachment, create B with same alias, exercise all owners | Every old path rejects before effect or quota | Does not require physical provider deletion |
| Updates preserve lifetime | Force-update config, reconnect provider, renew lease, start Turn | Same instance ID in registry and new records | Does not preserve binding revisions |
| Unstamped execution is read-only inside activated scope | Exercise status, backup, migration preview, resume, delivery, write, spend | Reads succeed with finding; ordinary business effects reject; explicit migration remains available | Non-activated legacy behavior is unchanged |
| Old writer is mechanically denied | Object-only, codec-only and enforcement packages; source/global/warm-process paths | Rejection before business effects, state/receipts unchanged | Decoder rejection alone is insufficient |
| Strict codec is exhaustive | Static writer inventory plus Python/TS conformance | No direct project-registry write bypass | Test helpers may use isolated fixtures |
| Source authority beats global projection | Make global entry stale and invoke every executable route | Source mismatch rejects | Global routing availability is separate |
| Preview is pure | Preview 1, 2, and 4 candidates | No file, registry, binding, or journal write | Read-only filesystem metadata access allowed |
| Plan binds full state | Change candidate, owner revision, registry, or Goal spec after preview | Apply rejects before backup or mutation | None |
| Path validation fails closed | Symlink, escape, duplicate path, non-regular or unreadable candidate | Preview/apply rejects | Canonical helper owns legal destination |
| Crash retry is idempotent | Every plan type; crash before/after each effect and journal acknowledgement | One prepared ID when applicable, exact reconciliation, no duplicate effect | Archive/delete-only is journaled too |
| Published-but-unsafe stays fenced | Crash after publication before required invalidation/source readback | Activation, mutation and quota reject; exact recovery works | Completed source with only projection lag is separately pending-sync |
| Cleanup failure is safe | Stamped and legacy bindings; changed legacy revision; local invalidation failure | Exact cleanup only; inert external records; required local invalidation retries | No invented retired identity |
| Receipt is complete | Compare receipt to owner table and all readbacks | Every owner/candidate/registry/global/backup/fence row present | Raw private contents excluded |
| Backup precedes destruction | Inject failure before and after backup verification | No destruction before proof; verified restore works | Archive-only still receives targeted backup |
| Rollback is fenced | Roll back before and after a post-resolution write | Early rollback restores orphan fence; late rollback rejects | Never restores old authority |
| Diagnostics remain unhealthy | Create orphan, legacy, mismatch, stale global, and incomplete journal states | Typed findings and exact next action | Diagnosis performs no repair |
| Commit race is fenced | Pause A after match, retire/recreate B, resume A; race rollback with a write | No B-side write, debit, delivery or ownership change | Real local entrypoint; real isolated provider for affected canonical paths |
| History stays historical | Same alias and operation key across lifetimes; delayed accepted result | A receipt/result cannot authorize or settle B | Historical A readback stays available |
| Default-off parity | Same input with activation off/on against immutable pre-change baseline | Off preserves schema, guidance, state and effects; on rejects stale refs | No live project migration |
| Provider boundary is honest | Native authority candidate presented to file-only resolution | No file-only adoption; existing import route identified | No D1–D3 or R6 qualification claimed |
| Product recovery is complete | CLI and packaged frontend; qualified Lark route | Preview → confirmation → crash/resume → source and original-entry readback | Untested transports explicitly remain unqualified |
The implementation must also run existing bootstrap, registry, deletion, session, host, Goal Channel, heartbeat, quota, backup, docs-governance, and public-safety suites. Skipped owner rows are not acceptance.
10. Operational contract¶
The stable findings are:
orphaned_goal_stateorphan_resolution_in_progresslegacy_goal_identitygoal_instance_id_missinggoal_instance_mismatchgoal_not_registeredstale_global_goal_instanceunsupported_registry_writer_protocolbinding_owner_inventory_changedmanual_cleanup_required
Every rejection identifies the source registry, Goal alias, expected and observed instance when public-safe, owner, retryability, and one lifecycle next action. It does not expose raw state, credentials, local session content, or provider secrets.
Status and diagnosis are read-only. They can inspect a legacy registry, historical binding, strict envelope, and incomplete journal. They cannot stamp identity, repair a global projection, choose a candidate, or complete cleanup.
CLI, packaged frontend and Lark consume the same preview/confirm/resume result. Show selected/competing candidates, execution block, backup proof, pending sync and the exact next action. A stale session should explain the retired lifetime and offer an explicitly authorized new binding, never silently resume it. Return completion to the initiating conversation. Reuse existing settings and action editors; a CLI-first slice must name unqualified companion surfaces and cannot claim the complete product journey. This RFC itself changes no UI.
The lifecycle retains one verified backup and one terminal receipt per resolution according to the existing backup retention policy. An incomplete journal is never garbage-collected automatically. Local cleanup retry is idempotent. External manual cleanup is advisory because logical revocation already prevents execution.
11. Normative delivery plan¶
| Milestone | Outcome and prerequisite | Decisive exit / rollback |
|---|---|---|
| M0: shipped codec prerequisite | Reuse #4917 codec/transaction/I/O census; no lifetime enforcement | v1 read/write support is shipped, not proof of old-writer exclusion. Do not rebuild or remove the now-used codec. |
| M1: characterize owners and typed rules | Inventory binding/effect producers and consumers; characterize baseline; implement only rules used by the selected lifecycle | Default-off parity, typed negative matrix, source/global/warm-process compatibility census. No minting or automatic activation. |
| M2: opt-in local lifetime transaction | Incompatible activation + retirement/recreation guard + selected local owner enforcement as one usable slice | Real CLI local ABA/concurrency/crash/replay path; retain M2/M3 activation hold until every supported effect owner is fenced. Before cutover abort; afterward forward repair. |
| M3: supported-owner qualification | Complete host, quota, Todo/lease, handoff, channel and automation paths for the activated profile | Every inventory row qualified, including delayed results and unsupported binaries; no legacy fallback inside activated projects. Non-activated behavior unchanged. |
| M4: orphan recovery | Reuse fence/diagnosis/path/backup owners; journaled file-state resolution with exact legacy cleanup | Preview, destructive/retry/rollback negatives and original-entry readback. Archive/delete can ship earlier without identity creation; native-provider adoption stays blocked. |
| M5: migration and product acceptance | M2–M4 plus multi-Goal quiescence/reconnection; packaged frontend and qualified Lark | 2–3 workers, dependency artifact, interrupted A, recreated B and late A return; unrelated Goal progresses; B accepted independently. No duplicate protected effect. |
M0 is shipped; retain the implemented source-session lifetime transaction and qualified binding owners. Continue from the remaining owner/compatibility gaps in the binding inventory and the M4 recovery path tracked by #5206; do not restart the codec or count fields as the ABA outcome. M2 and M3 describe implementation order, not permission to activate a partially fenced system. R2/R3 consume the completed local slice; R6 service adoption and D1–D3 provider promotion retain their own acceptance. No new paid cohort or soak is authorized.
Product integration through existing roadmap journeys¶
Use #5206 for lifetime and recovery delivery and the existing R1–R3/G1 owners under #4574 for product adoption. The golden-query lifecycle variant is the common scenario specification. These are planned integration exits, not claims that the source profile is activated or that G1/M5 has passed.
| Priority / existing journey | Lifetime contribution and companion owner | Decisive observable result |
|---|---|---|
| P0 · GQ01/02, R1/R2: create a Goal and create/reuse an Agent | Source lifecycle publishes the exact GoalRef; registry/onboarding and session owners reconcile creation retries and bind authorized work | Lost responses and repeated clicks create no duplicate Goal, Agent or executor; an old same-alias attachment cannot attach itself to the new Goal |
| P0 · GQ05/11/12, R2/R3/G1: dependent artifacts and independent review | Collaboration carries instance identity through request, adoption, result and original-conversation return; Todo/lease and quota retain their own acceptance/effect checks | Two real cycles use exact artifact versions; late A work cannot complete, debit or enter B's accepted synthesis; B and unrelated work still progress |
| P0 · GQ08/09, R2/R3: stop, correct and resume | Session/execution generation and claim/lease fence stale execution within one Goal; this RFC fences a retired Goal lifetime | Ordinary correction/reconnect/resume retains the GoalRef; explicit retirement/recreation changes it; both paths preserve the owed result without duplicate effects |
| P1 · GQ15, S7/R2/R3: mixed team within an agreed budget | #5389 isolates quota spend/replay/repair/void/readback by instance; existing quota and scheduler owners enforce team allocation | Replay does not double-charge or charge a successor; the team allowance is not copied to each worker. Instance accounting alone does not qualify shared-budget allocation |
| P1 · R5/M4/M5: recover orphaned work at create/connect entry | Existing diagnosis and lifecycle resolution drive preview, backup, apply/resume and explicit reconnection | The packaged App and CLI show the selected disposition and recovery result; no guessed candidate or silent adoption of old authority |
| P2 · GQ16, R6/G3: local/cloud work and reconnect | Authenticated service, remote binding, revocation and lease owners consume exact identity after their own qualification | A returning remote executor cannot commit or debit a successor; local-file evidence does not qualify a remote provider |
Keep Goal lifetime, registered Agent identity, host session/execution generation and work request/attempt identity distinct. Reusing an Agent grants no new Goal authority; changing its model, reconnecting or opening a Turn does not mint a Goal instance. Intent corrections and artifact revisions retain their existing R4/work-graph owners. Identity matching neither transfers a lease nor accepts an artifact. No new Agent factory, scheduler or parallel task ledger is needed.
First qualify creation/reuse, two collaboration cycles and same-lifetime
interruption on an already supported profile. In an isolated qualification
environment, add the M5 retirement/recreation variant only after the selected
source profile's M2/M3 owner, old-writer and effect-drain gates are satisfied;
include M4 if orphan resolution is exercised. Component fixtures may run before
that gate but cannot certify the live journey. Use the packaged App with
independent CLI/source readback; Lark retains separate transport/audience
qualification. Record entry/retry, receiver adoption, artifact acceptance,
instance-scoped settlement and original-route return separately. #5389 qualifies
only quota_settlement; missing companion owners remain open in #5206.
12. Open decisions and holds¶
- Supported package/profile matrix: release and host owners pin all supported object-only, codec-only and enforcement packages and exclusion evidence before M2 activation. v2 names are proposed; protocol support requires semantics.
- Exact commit guard: The M2 source-session candidate uses one alias-scoped cross-runtime lock around the project-registry transaction. M3 must still prove the external-effect drain contract before activation. A digest recheck alone cannot discharge that hold.
- Canonical destination/provider import: reuse the current path owner; follow #4915 without assuming merge. Provider-state adoption needs its own reviewed import contract; the first file-only slice rejects it.
- Service profile: R6 chooses authenticated identity projection and server serialization. No remote local-path requirement or cloned writable registry.
- Retention/default rollout: existing backup/receipt owners decide retention; incomplete journals are never automatically collected. Fleet-wide enforcement or default activation requires a separate disclosed migration/release decision.
Appendix A: Execution ledger (non-normative)¶
2026-09-23 — Design synthesis¶
- Baseline:
23edcb19c70394480e3a9ebe8a960f5a320c5342 - Delivered: Revised RFC proposal; M0 codec prerequisite shipped separately.
- Evidence: Current registry, bootstrap, deletion, binding, orphan-fence, backup, global-projection, and documentation contracts were inspected.
- Known gaps: No instance enforcement, activation or resolution fixture has shipped.
- Effect on normative design: Align with roadmap/TS/shared authority; separate codec compatibility from enforcement, specify commit fencing and legacy cleanup.
2026-09-23: M2 source-session implementation candidate¶
- Baseline:
cbbdd837f65c8ba28161115cc4ce39093bfa2951 - Proposed: A fresh-project-only
source_session_v1profile, exact bind and unbind receipts, journaled A-to-B recreation, and read-only exact resolution. - Evidence: Real CLI tests cover ABA ordering, pre-publication retry, post-publication forward repair, exact-byte replacement rollback, operation ID conflicts, capacity rejection, and replay at capacity.
- Remaining hold: Every result has
execution_authority: false. M3 must qualify the remaining effect owners before existing-project activation or global routing can open.
2026-09-26: M3 attached-host Chat candidate¶
- Baseline:
9849366c6. - Proposed: Bind attached Chat sessions to the current exact GoalRef and recheck it for enqueue, resume, and new claims under the M2 lifetime guard. A delayed result may complete only when its persisted claim admission still matches the historical session.
- Compatibility: Non-source profiles keep the existing writers, lookup,
broker payloads, and serialized bytes. Clients do not submit
goal_instance_id. - Remaining hold: This qualifies only
attached_host_chat_session. Managed provider startup and downstream host effects remain blocked by the separatefirst_party_host_runtimerow and the overall M3 activation hold.
2026-09-27: first-party Host runtime partial enforcement¶
- Baseline:
fd96e5e2574272262b9ea604a96581a0d20e94d1 - Delivered: A TypeScript-owned exact GoalRef decision and alias-scoped lifecycle guard for source-profile Turn journals, Codex descriptors, DSH session identity, and the Kunlun native runtime journal.
- Evidence: Negative tests cover Goal A results returning after same-alias Goal B publication, cached Turn-result recovery, legacy Host state, cross-instance session selection, and serialized result/recreation commits. Non-source plans, paths, schemas, and persisted bytes retain legacy behavior.
- Remaining hold: This is partial M3 enforcement. Accepted-before-retirement
downstream drain, unsupported/warm binaries, and the remaining inventory
owners are not qualified.
execution_authority: falseand the M3 activation hold remain unchanged.
2026-09-29: M3 handoff inbox/outbox candidate¶
- Baseline:
738115bde87eef3fd153abe456d53da5e2b249f8. - Proposed: Bind each new shadow-management lineage to the exact source GoalRef. Existing outbox entries, commit markers, and cursors remain byte-compatible; their entry identity already includes the immutable lineage. Exact bootstrap, management, and outbox-manifest records use new schema versions. An exact caller cannot infer ownership from a legacy binding.
- Evidence: A real source-session A-to-B recreation leaves Goal A's pending files and candidate head unchanged when B drains. B cannot capture against A's active lineage. A capture started by A is revalidated under the Goal lifetime guard and rejected before its primary write if B has replaced A. Explicit rollback archives A with its GoalRef, and a fresh B bootstrap accepts and replays only B work. A legacy drain request cannot enter an exact lineage.
- Remaining hold: This qualifies only the
handoff_inbox_outboxinventory row. Todo/lease mutation admission, quota, automation, Goal Channel, and the overall M3 activation hold remain separate.
2026-09-30: M3 Turn-journal exact commit candidate¶
- Baseline:
350f0f326. - Proposed: Keep
loopx_turn_journal_v0and its existing path. Source-profile plans carry matching exact GoalRef copies in the plan and transaction. Every executor journal mutation uses one persistence callback that hands the alias-scoped lifecycle guard from Python to TypeScript. TypeScript claims and verifies that witness, reusesdecideFirstPartyHostRuntime(require_current), then takes the existing journal mutation lock and commits before releasing the source guard. - Evidence: TypeScript owner tests reject missing, malformed, mismatched, expired and stale source admission before journal mutation. A real Python-to-TypeScript integration commits and replays Goal A, publishes same-alias Goal B, proves a later A checkpoint leaves both files unchanged, and commits B independently.
- Compatibility: Read-only inspection and recovery still read legacy journals. Non-source writes retain the old RPC shape and persisted bytes. Source admission facts and lock tokens are transport-only and never persist.
- Remaining hold: This qualifies only the
turn_journalinventory row. It does not completefirst_party_host_runtime, downstream external-effect drain, unsupported/warm binary coverage, or any other M3 row.execution_authority: falseand the overall activation hold remain.
2026-09-30: M3 source Turn effect admission and drain candidate¶
- Baseline:
3ec049e138917a8cce4f84197ba196d26445b2b0. - Delivered: Source-profile Turn settlement now records a per-effect
admission before durable writeback, quota spend, or terminal closeout. The
existing alias guard covers admission plus the prepared journal checkpoint,
and later covers the committed or aborted checkpoint plus admission release.
The final provider admission remains held through scheduler apply and the
optional post-settlement observer. A durable
source_effect_holdmarker keeps crashes andscheduler_action_requiredresumable, and the admission releases only with the final journal checkpoint. Provider calls, readbacks, and tail callbacks remain outside the alias guard. - Retirement: Recreation first closes the exact Goal A gate. It returns
drain_requiredwhile an admitted effect still needs provider readback and publishes Goal B only after the admission set is empty. A committed readback checkpoints once, an absent readback aborts without provider re-execution, and an unknown readback keeps Goal A current. - Evidence: Deterministic thread and crash tests cover provider commit before checkpoint, close versus next-step admission, recreation during scheduler and post-settlement callbacks, and committed, absent, and unknown readbacks. Existing source-session recreation and non-source Turn paths retain their schemas and behavior.
- Remaining hold: This qualifies the built-in source Turn settlement slice
of
first_party_host_runtime. Other Host effects, unsupported or warm binaries, and every other partial inventory owner remain blocked.execution_authority: falseand the overall M3 activation hold remain.
2026-09-30: M3 quota settlement owner candidate¶
- Baseline:
3ec049e13. - Proposed: Bind source-profile quota spend, replay, receipt repair, void,
settlement readback, and rolling-window accounting to the caller-captured
exact GoalRef. Python hands the ordered run-index and Goal-lifecycle lock
witnesses to the TypeScript accounting owner. TypeScript validates both
witnesses and reuses
decideFirstPartyHostRuntime(require_current). Single-phase accounting adopts the witnesses through artifact commit; multi-phase monitor accounting borrows them while Python retains the enclosing lock scope across preflight, provider writeback, and commit. - Evidence: TypeScript and Python integration tests publish same-alias Goal B after Goal A capture and prove that stale A writes nothing. They also cover B-only spend and void, cross-instance replay and prepared-receipt repair rejection, exact settlement readback, per-instance rolling-window accounting, mixed alias/exact fallback history, and exact auxiliary monitor preflight, exception, commit, and replay under one admission.
- Compatibility: Non-source spend, replay, void, and readback requests omit GoalRef and source admission. Their persisted records, receipts, response payloads, and lock behavior retain the legacy shape.
- Remaining hold: This qualifies only the
quota_settlementinventory row. The source-profile provider journey remains behind its existing activation gate; the native monitor evidence does not claim that route is activated. Unsupported and warm binaries, downstream external-effect drain, and every other unqualified M3 owner remain blocked.execution_authority: falseand the overall activation hold remain unchanged.
Appendix B: Decision log¶
| Date | Decision | Owner / approval | Alternatives | Normative sections changed |
|---|---|---|---|---|
| 2026-09-23 | Propose source-owned random Goal instance identity plus strict registry envelope | Proposal; maintainer approval pending | Counter, path identity, additive capability marker | Initial RFC |
| 2026-09-23 | Refine compatibility, typed lifecycle ownership, commit fencing and legacy cleanup against current main | Design proposal; implementation holds in Section 12 | Decoder-only exclusion, duplicate Python state machine, guessed legacy identity | Sections 3–5 and 7–12 |
Appendix C: Evidence registry¶
| Evidence id | Claim | Baseline / environment | Artifact or command | Result | Privacy / validity boundary |
|---|---|---|---|---|---|
| E1 | M0 reads/writes v1 without lifetime enforcement | Named baseline / #4917 | Registry codec and bootstrap inspection | observed | Codec compatibility is not identity admission |
| E2 | The shipped fence blocks orphaned guided bootstrap but does not resolve it | Named baseline | Issue #4801 and PR #4808 | observed | Public issue and code scope |
| E3 | Global entries are source projections | Named baseline | loopx/global_registry.py inspection |
observed | Static current-code fact |
| E4 | Activation excludes every supported old effect path | future M1–M3 fixture | Object-only/codec-only/enforcement matrix | unverified | Source/global/warm-process coverage required before activation |
| E5 | Every durable binding owner validates exact identity | future M1-M3 fixtures | Owner inventory conformance suite | unverified | Required before enforcement |
| E6 | Resolution is crash-safe and recoverable | future M4 fixtures | Fault-injection and restore matrix | unverified | Required before destructive use |
Appendix D: Rejected or superseded alternatives¶
- A warning-only writer capability marker cannot constrain code that does not understand the marker.
- A redirect object leaves the old path writable and permits split authority.
- A global counter makes the projection an allocator or requires permanent tombstones.
- Automatic binding upgrade guesses history from an alias and is forbidden.
- Provider-wide cleanup as a commit precondition couples availability to every integration and still cannot prove external deletion.
- Runtime-directory migration does not solve non-file bindings and is outside issue #4801.
Appendix E: Incident and review lessons¶
- A healthy current state does not prove continuity from an earlier same-name Goal.
- An advisory compatibility check is not a writer fence when old code ignores it.
- Crash safety requires journaling every plan before the first mutation, reserving identity when applicable, and reconciling effects whose acknowledgement was lost.
- Cleanup and authorization are separate: stale records may remain for audit while exact identity matching keeps them inert.