Implementation Plan: Coord/lane actor-identity representation cluster
Branch: fix/actor-identity-representation | Date: 2026-09-18 | Spec: spec.md Input: Feature specification from kitty-specs/actor-identity-representation-01M2T0ER/spec.md
Summary
Reconcile how an acting agent's identity is represented across the claim, verdict, and ownership surfaces so four defects stop firing: a fresh full-identity implement claim self-rejecting (#4665), an agent review verdict attributed to the git user (#4670), a fix-mode claim after rejection blocked by stale reviewer ownership (#4673), and move-task --agent not persisting the acting identity (#3029). The technical approach is narrow and CLI-local: reconcile the two identity representations in the CLI-owned comparison layer (not the byte-identical shared projection), thread the claimed reviewer identity through a single completion-command render seam plus an event-log resolver, and fix the CLI-owned rejection-release/ownership path — leaving the shared spec-kitty-events reducer untouched and filing a scoped upstream follow-up only if a residual reducer fold-ordering contribution to #4673 is proven.
Technical Context
Language/Version: Python 3.11+ Primary Dependencies: typer, rich (CLI); spec-kitty-events (PyPI-pinned >=9,<10, currently 9.1.6 — read-only external contract, not editable here) Storage: Append-only status event log (status.events.jsonl) reduced to a snapshot; no new storage Testing: pytest (tests/status/, tests/specify_cli/cli/commands/agent/, tests/specify_cli/cli/commands/agent/review/), @pytest.mark.regression red-first per ADR 2026-07-17-1 Target Platform: Linux/macOS/Windows CLI Project Type: single (CLI library) Performance Goals: no regression to CLI < 2s typical; the fix adds no new full-log reads Constraints: additive-only event history (no rewrites); identity key stays a bare string (generic-actor in test); shared projection stays byte-identical to upstream; complexity ≤15; ruff + mypy --strict + ruff format clean, zero new suppressions Scale/Scope: 4 issues, ~4–6 CLI-owned source files, one execution lane
Key seams (grounded, file:line)
- Identity projection (byte-identical local ↔ upstream, leave unchanged):
src/specify_cli/status/models.py:152actor_identity_str/:119decode_actor; upstreamspec_kitty_events/diary.py:236(reducedactorslot written atdiary.py:1109). - Impl-claim comparison layer (#4665 fix seam, CLI-owned):
src/specify_cli/status/work_package_lifecycle.py:103_actor_key/:111_actors_compatible/:49GENERIC_IMPLEMENTATION_ACTORS/ claim branches:165/:203/:231. - Divergent claim-emit sites (#4665 whack-a-field): compact-string claim
src/specify_cli/cli/commands/implement.py:1881(effective_actor) →_start_wp_implementation_status; structured-dict claimsrc/specify_cli/cli/commands/agent/workflow_executor.py:744_implement_start_claim→status/emit.py:1305build_self_asserting_actor. - Review role channel (reviewer-vs-implementer distinctness, NOT the identity key):
work_package_lifecycle.py:328review_claim_decision(degrades to ALLOW on stale/None role). - Completion-command render (#4670 whack-a-field, ≥5 sites):
workflow_executor.py:1960/2003/2124,tasks_verdict_persistence.py:893,tasks_transition_core.py:703(+ help texttasks.py,next_cmd.py). - Verdict actor resolution (#4670):
tasks_move_task.py:2122(st.actor = st.agent or "user"), reviewer resolver_mt_resolve_reviewer_identity(used only for the rejected-cycle artifact today). - Ownership authorities (#4673, three slots — C-006): transition
actor(overwritten each hop); runtimeagentreleased on rejection (_CLAIM_RELEASE_SLOTS, upstreamdiary.py:1042; move-task reads it attasks_move_task.py:355, fallback:2329); sticky resolved-bindingrole(_RUNTIME_SLOTS, upstream, NOT released). Rejection re-stamp:tasks_move_task.py_mt_emit_runtime_state/_build_claim_review_override.
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
Charter (.kittify/charter/charter.md) present. Relevant gates:
- Single canonical authority (Governing Principle; DIRECTIVE_044): the mission is unification, not parity — reconcile the two identity representations to one canonical comparison rather than adding a third. PASS by design.
- ATDD-first / red-first (C-011; DIRECTIVE_041/034; ADR 2026-07-17-1): each defect lands an issue-pinned
@pytest.mark.regressionRED on the WP'splanning_base_branch, GREEN after (NFR-003). PASS — encoded per WP. - Architectural alignment / shared-package boundary (DIRECTIVE_001; shared-package ADR 2026-04-25-1):
spec-kitty-eventsis a read-only external contract; no vendoring or editing (C-001/C-005). PASS. - Reviewer ≠ implementer (Standing Order #8): each WP reviewed by a distinct role; issue-matrix row + verdict per issue. PASS — process.
- Terminology canon (Mission not Feature): no new user-facing
feature*identifiers; error/help strings use--mission. PASS — reviewer greps changed lines. - No version numbers in scope (DIRECTIVE_045): none introduced. PASS.
- Locality of change / smallest-viable-diff (DIRECTIVE_024/025; RECONCILE_CHANGE_SCOPE_TENSIONS): fix at the CLI comparison/render/ownership seams; do not refactor the reducer or the projection. PASS.
No violations to justify in Complexity Tracking.
Project Structure
Documentation (this mission)
kitty-specs/actor-identity-representation-01M2T0ER/
├── plan.md # This file
├── research.md # Phase 0 output
├── data-model.md # Phase 1 output
├── quickstart.md # Phase 1 output
├── contracts/ # Phase 1 output (behavioral contracts)
├── traces/ # tooling-friction / approach / design-decisions
└── tasks.md # Phase 2 output (/spec-kitty.tasks — NOT created here)
Source Code (repository root)
src/specify_cli/
├── status/
│ ├── models.py # actor_identity_str/decode_actor — LEAVE UNCHANGED (C-005)
│ ├── work_package_lifecycle.py # WP01: _actor_key/_actors_compatible reconciliation; WP03: rework claim
│ └── emit.py # build_self_asserting_actor (claim-emit shape)
└── cli/commands/
├── implement.py # WP01: route workspace-create claim through the canonical actor shape
└── agent/
├── workflow_executor.py # WP01: _implement_start_claim; WP02: completion-command render seam
├── tasks_move_task.py # WP02: verdict actor resolution; WP03: rejection-release/ownership; WP04: --agent persistence
├── tasks_verdict_persistence.py # WP02: render/verdict actor
└── tasks_transition_core.py # WP02: render; ownership guard (read-only reference)
tests/
├── status/ # WP01/WP03 unit + reducer-adjacent
└── specify_cli/cli/commands/agent/ # WP01/WP02/WP03/WP04 integration
└── review/ # WP02 review claim→handoff→completion
Structure Decision: Single CLI library. All edits are CLI-owned modules under src/specify_cli/. The shared spec-kitty-events reducer and the byte-identical projection helper are explicitly out of the editable set (C-001/C-005).
Complexity Tracking
No Constitution Check violations — table intentionally empty.
Parallel Work Analysis
Dependency Graph
WP01 (#4665 identity-key reconciliation) ──► WP02 (#4670 reviewer attribution)
│ └─► WP03 (#4673 fix-mode ownership)
└───────────────────────────────────► WP04 (#3029 move-task --agent persistence)
WP01 defines the reconciled identity semantics the others consume, so it lands first.
Work Distribution — expect ONE lane (honest collapse)
finalize-tasks unions WPs into lanes by owned-file OVERLAP, not dependency. The overlap graph is fully connected: WP01↔WP03 share work_package_lifecycle.py; WP02↔WP03↔WP04 share tasks_move_task.py; WP01↔WP02 share workflow_executor.py. Therefore all four WPs collapse into a single lane and execute serially in dependency order WP01 → {WP02, WP03, WP04}. This is authored deliberately — do NOT declare parallel lanes that finalize will merge anyway (avoids the post-finalize planning-sha staleness footgun). No cross-lane coordination points exist because there is one lane.
Coordination Points
own red-first regression, reviewed independently (reviewer ≠ implementer).
it lands a characterization regression + evidence and closes #3029 rather than changing code.
- Single lane ⇒ no inter-lane sync. Each WP is a distinct commit (or small commit set) with its
- WP04 (#3029) begins with a live re-verification on the base; if the behavior is already green,