Skip to content

fix(p0): realign Bridge–TUI JSON schema with PRD-mandated minimum fields#25

Merged
SolshineCode merged 1 commit into
mainfrom
fix/p0-bridge-tui-schema-realignment
May 8, 2026
Merged

fix(p0): realign Bridge–TUI JSON schema with PRD-mandated minimum fields#25
SolshineCode merged 1 commit into
mainfrom
fix/p0-bridge-tui-schema-realignment

Conversation

@SolshineCode
Copy link
Copy Markdown
Owner

Closes P0 #2 from the 2026-05-08 CTO audit. All 13 PRD §"Bridge–TUI Interface Contract" line-231 minimum per-session fields now emitted. 13 new compliance tests; 180 total. Refs PR #22, kanban t_26404be3.

Closes P0 #2 from the 2026-05-08 CTO audit. The audit found 8 of 13
PRD-mandated per-session fields were missing from the on-disk JSON,
leaving the M0-published "binding contract" only nominally enforced.

bridge/session_state.py — Session gains:
- last_event (string | null) — name of most recent hook event
- last_event_ts (ISO-8601 | null) — timestamp of last_event
- is_manual_injection (bool) — True when a state change came from the
  TUI's manual-injection bindings rather than a real hook event (BR-11)
- to_dict now also emits PRD-mandated aliases:
    state           = status
    momentum_streak = momentum
- All three new fields preserved through SessionState.from_dict
  (round-tripped through SaveExchange).

bridge/scoring.py — SimpleScoringStrategy gains
calculate_session_breakdown() returning the six PRD-mandated
per-turn breakdown fields (base_value_this_turn, token_bonus,
plan_bonus, goal_bonus, net_payout, momentum_multiplier). Returns
all zeros for WAITING/INACTIVE/COMPLETED/SUSPENDED. Treats REDIRECTED
like ACTIVE for base/token/goal (no clawback) but momentum_multiplier
is 0 because momentum is 0.

bridge/save_exchange.py — write_payload now enriches every session's
dict with the breakdown + per-session schema_version pin, plus
schema_version at the top level.

bridge/hook_listener.py — feed() takes is_manual=True kwarg; updates
the session's last_event / last_event_ts / is_manual_injection on
every event.

bridge/tests/test_prd_schema.py — 13 new tests:
- All PRD-required per-session fields present in on-disk JSON
- state == status alias, momentum_streak == momentum alias
- Active-session breakdown numbers exact (base=15, token=10, plan=15,
  goal=120 for the documented inputs; net=160+momentum)
- Waiting-session breakdown all zeros
- Redirected-session keeps base + goal + token, momentum=0 → mult=0
- Plan bonus capped at 2 plans (BR-6)
- Token bonus capped at 10 tokens (BR-8)
- last_event recorded on hook feed
- is_manual_injection propagates from feed() kwarg
- last_event survives SaveExchange round-trip
- schema_version emitted at root and per-session

docs/bridge-tui-schema.md — rewritten as the binding-contract doc
PRD §"Bridge–TUI Interface Contract" required. Documents all 13
PRD-mandated fields, the legacy/convenience fields kept alongside,
the six-value state enum, the versioning policy, and points at the
compliance test.

Total: 180 passing (was 167; adds 13).

Refs: CTO audit doc PR #22, kanban t_26404be3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@SolshineCode SolshineCode merged commit 69002f6 into main May 8, 2026
1 check passed
@SolshineCode SolshineCode deleted the fix/p0-bridge-tui-schema-realignment branch May 8, 2026 18:27
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements the Bridge–TUI Interface Contract by adding session metadata tracking, detailed reward breakdowns, and field aliases for backward compatibility. Key updates include modifying the HookListener to record event history, enhancing SaveExchange to include per-session scoring components, and introducing a compliance test suite. Review feedback suggests moving an inline import to the module level for better performance and refactoring duplicated scoring logic in the scoring engine to improve maintainability.

Comment thread bridge/hook_listener.py
if session_id:
session = self._find_session(session_id)
if session is not None:
from datetime import datetime as _dt
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Performing an inline import of datetime inside the feed method is inefficient, as this method is invoked for every hook event processed by the bridge. While Python's import system is optimized, the lookup still occurs on every call. It is better practice to move this import to the top of the module to follow standard Python conventions and improve performance.

Comment thread bridge/scoring.py
Comment on lines +118 to +161
def calculate_session_breakdown(self, session: Session) -> Dict[str, int]:
"""Returns the PRD §"Bridge–TUI Interface Contract" per-session breakdown.

Decomposes the aggregated reward into the named components the
PRD's binding contract enumerates: base_value_this_turn,
token_bonus, plan_bonus, goal_bonus, net_payout. Sessions that
contribute zero (WAITING/INACTIVE/COMPLETED/SUSPENDED) return
all zeros.
"""
breakdown = {
"base_value_this_turn": 0,
"token_bonus": 0,
"plan_bonus": 0,
"goal_bonus": 0,
"net_payout": 0,
"momentum_multiplier": 0,
}
if session.status not in (SessionStatus.ACTIVE, SessionStatus.REDIRECTED):
return breakdown

# Base reward (production + gold).
breakdown["base_value_this_turn"] = 10 + 5

# Token bonus (gold + science applied per token, capped at 10).
token_units = min(session.tokens, 10)
breakdown["token_bonus"] = token_units * 2 # +1 gold + +1 science per unit

# Plan bonus (production, capped at 2 plans).
breakdown["plan_bonus"] = min(len(session.plans), 2) * 15

# Goal completion bonus (gold + science).
breakdown["goal_bonus"] = session.goals_completed * 60 # +40 gold + +20 science

# Momentum multiplier — culture/10 from current momentum.
breakdown["momentum_multiplier"] = session.momentum // 10

breakdown["net_payout"] = (
breakdown["base_value_this_turn"]
+ breakdown["token_bonus"]
+ breakdown["plan_bonus"]
+ breakdown["goal_bonus"]
+ breakdown["momentum_multiplier"]
)
return breakdown No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The scoring logic implemented in calculate_session_breakdown (lines 138-160) is a direct duplication of the logic in calculate_session_reward (lines 85-109). This duplication increases the maintenance burden and the risk of logic divergence if scoring rules (such as the plan bonus or token cap) are modified in the future. Consider refactoring the scoring calculations into a shared internal method or using constants for the reward values to ensure consistency across the engine.

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.

1 participant