feat: length-aware reinforcement + batch triage CLI + off-host backup
- Reinforcement matcher now handles paragraph-length memories via a
dual-mode threshold: short memories keep the 70% overlap rule,
long memories (>15 stems) require 12 absolute overlaps AND 35%
fraction so organic paraphrase can still reinforce. Diagnosis:
every active memory stayed at reference_count=0 because 40-token
project summaries never hit 70% overlap on real responses.
- scripts/atocore_client.py gains batch-extract (fan out
/interactions/{id}/extract over recent interactions) and triage
(interactive promote/reject walker for the candidate queue),
matching the Phase 9 reflection-loop review flow without pulling
extraction into the capture hot path.
- deploy/dalidou/cron-backup.sh adds an optional off-host rsync step
gated on ATOCORE_BACKUP_RSYNC, fail-open when the target is offline
so a laptop being off at 03:00 UTC never reds the local backup.
- docs/next-steps.md records the retrieval-quality sweep: project
state surfaces, chunks are on-topic but broad, active memories
never reach the pack (reflection loop has no retrieval outlet yet).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -476,6 +476,60 @@ def test_reinforce_matches_at_70_percent_threshold(tmp_data_dir):
|
||||
assert any(r.memory_id == mem.id for r in results)
|
||||
|
||||
|
||||
def test_reinforce_long_memory_matches_on_absolute_overlap(tmp_data_dir):
|
||||
"""A paragraph-length memory should reinforce when the response
|
||||
echoes a substantive subset of its distinctive tokens, even though
|
||||
the overlap fraction stays well under 70%."""
|
||||
init_db()
|
||||
mem = create_memory(
|
||||
memory_type="project",
|
||||
content=(
|
||||
"Interferometer architecture: a folded-beam configuration with a "
|
||||
"fixed horizontal interferometer, a forty-five degree fold mirror, "
|
||||
"a six-DOF CGH stage, and the mirror on its own tilting platform. "
|
||||
"The fold mirror redirects the beam while the CGH shapes the wavefront."
|
||||
),
|
||||
project="p05-interferometer",
|
||||
confidence=0.5,
|
||||
)
|
||||
interaction = _make_interaction(
|
||||
project="p05-interferometer",
|
||||
response=(
|
||||
"For the interferometer we keep the folded-beam layout: horizontal "
|
||||
"interferometer, fold mirror at forty-five degrees, CGH stage with "
|
||||
"six DOF, and the mirror sitting on its tilting platform. The fold "
|
||||
"mirror redirects the beam and the CGH shapes the wavefront."
|
||||
),
|
||||
)
|
||||
results = reinforce_from_interaction(interaction)
|
||||
assert any(r.memory_id == mem.id for r in results)
|
||||
|
||||
|
||||
def test_reinforce_long_memory_rejects_thin_overlap(tmp_data_dir):
|
||||
"""Long memory + a response that only brushes a few generic terms
|
||||
must NOT reinforce — otherwise the reflection loop rots."""
|
||||
init_db()
|
||||
mem = create_memory(
|
||||
memory_type="project",
|
||||
content=(
|
||||
"Polisher control system executes approved controller jobs, "
|
||||
"enforces state transitions and interlocks, supports pause "
|
||||
"resume and abort, and records auditable run logs while "
|
||||
"never reinterpreting metrology or inventing new strategies."
|
||||
),
|
||||
project="p06-polisher",
|
||||
confidence=0.5,
|
||||
)
|
||||
interaction = _make_interaction(
|
||||
project="p06-polisher",
|
||||
response=(
|
||||
"I updated the polisher docs and fixed a typo in the run logs section."
|
||||
),
|
||||
)
|
||||
results = reinforce_from_interaction(interaction)
|
||||
assert all(r.memory_id != mem.id for r in results)
|
||||
|
||||
|
||||
def test_reinforce_rejects_below_70_percent(tmp_data_dir):
|
||||
"""Only 6 of 10 content tokens present (60%) → should NOT match."""
|
||||
init_db()
|
||||
|
||||
Reference in New Issue
Block a user