feat(affinity): Bond/Chemistry lines, tiered labels, sparser eval distribution#132
Merged
Conversation
Design spec for splitting the relationship label into engine-owned Bond (friendship) and Chemistry (romance) lines, each with 4 tiered labels (+ stranger via the legacy field), an exponential-feel bar curve, per-turn label transitions stored on companion_affinity_events and served by the BFF, and an asymmetric/sparser six-axis eval distribution. 6-axis base unchanged. Defers the filter_prompt override (#130). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Refine the persistence approach: bond/chemistry are Postgres GENERATED ALWAYS ... STORED columns kept in lockstep with the 6 axes, instead of engine-written values. Removes the load_or_create/record_ghost special-casing and the default-mismatch risk. relationship_label and the per-turn label_changes stay engine-written. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Composite uses warmth.max(0) (no 0.5 baseline); migration 0029 lowers the new-row default seed (warmth 0.1, trust/intrigue/tension 0) so a fresh session reads as stranger with near-empty bars while keeping a neutral opening tone. Existing rows and axis mechanics unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
…ef 1) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
…bel_changes Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
…transition DTOs Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Full rewrite of docs/affinity-model.md + .zh.md covering 6-axis base, bond/chemistry folding formulas (warmth floored), tiers (0.15/0.35/0.62), even-25%-band bar curve, 8 tier keys, legacy relationship_label mapping (stranger/frenemy retired logic), eval distribution (most-turns-0, +0.4/-0.6 asymmetric cap), persistence (generated cols + lowered seed + label_changes), and API surfaces (AffinitySnapshot bars+labels, BFF effective_deltas_computed + label_changes). Light touch on docs/api-reference.md + .zh.md: updated affinity snapshot JSON examples and BFF event response to show new fields. Regenerated openapi.json: adds BondChemistryDeltas, LabelTransitionDto, TurnLabelChangesDto schemas; AffinitySnapshot bond/chemistry/bond_label/ chemistry_label fields; BffAffinityDelta/AffinityEventEntry computed+changes. Also includes cargo fmt reflows from earlier tasks and a one-line fix to the debug_affinity_returns_vector_for_owner test whose expected warmth/intrigue defaults still referenced the old seed (0.3/0.5) instead of migration-0029 values (0.1/0.0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Codex review (PR #132, P2): the snapshot read relationship_label from the stored DB column while bond/chemistry + their labels are computed fresh from the axes. A freshly-created row (column still NULL) returned null, and a pre-migration row returned a stale old-heuristic value (even frenemy) — both contradicting the new lines and defeating the "fresh session = stranger" goal. Derive it via legacy_relationship_label() on read, like the other label fields. Field stays Option<String> (no OpenAPI change). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
Codex review (PR #132, P2): effective_deltas_computed linearly folded the per-axis delta and ignored the max(warmth,0) floor, reporting phantom bond/chemistry gains when warmth<0. Now computed at persist time from the floored before/after scores, stored on companion_affinity_events .effective_line_deltas, and read by the BFF/debug /event endpoints (like label_changes). Drops BondChemistryDeltas::from_axis_deltas. Signed-off-by: enriquephl <70942788+enriquephl@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Splits the relationship state into two engine-owned derived lines — Bond (friendship) and Chemistry (romance) — folded from the unchanged 6-axis affinity vector. Each line gets 4 value-derived tier labels; the legacy
relationship_labelis preserved for back-compat. The six-axis evaluator is made sparser and asymmetric so progress feels earned. The 6-axis base mechanics (EMA, time-decay) are unchanged; only the new-row default seed is lowered.Spec:
docs/superpowers/specs/2026-06-30-affinity-bond-chemistry-tiers-design.mdWhat changed
bond = (max(warmth,0)+trust+intrigue)/3,chemistry = (max(warmth,0)+intimacy+tension)/3. warmth floored at 0 (no 0.5 baseline); patience excluded (rule-owned).0.15 / 0.35 / 0.62), mapped to even 25% bands → fast early, grind near 100%.bond_label(acquaintance→confidant),chemistry_label(spark→lover). Legacyrelationship_labelnow = the higher line mapped to an old name (stranger when both tier 1;frenemyretired from emission, kept parseable) — replaces the tangledinfer_labelheuristic.−0.6 / +0.4(losses bigger/easier than gains); prompt rewritten so ordinary turns score 0, gains are rare-but-large, losses fire more readily. EMA + decay untouched.companion_affinity_events.label_changes(NULL when no tier moved), and served by the BFF + debug/eventendpoints alongside a foldedeffective_deltas_computed.bond/chemistryas PostgresGENERATED ALWAYS … STOREDcolumns (auto-populate existing rows, can't drift); lowered new-row default seed (warmth 0.1, trust/intrigue/tension 0) so a fresh session reads as stranger with a neutral opening tone;label_changes JSONBcolumn.AffinitySnapshotgainsbond/chemistrybar fills + labels; new shared DTOs; OpenAPI snapshot refreshed.affinity-model.md/.zh.md;api-referenceupdated.Out of scope
filter_promptoverride foraffinity_evaluation— deferred, tracked in #130.Testing
cargo fmt --all+clippy --workspace --all-targets -D warningsclean.SET DEFAULTaffects new rows only,label_changesis nullable — existing rows untouched.🤖 Generated with Claude Code