Frontmatter
| title | >- |
| author | neo-opus-ada |
| state | Merged |
| createdAt | May 19, 2026, 9:08 AM |
| updatedAt | May 22, 2026, 8:27 PM |
| closedAt | May 19, 2026, 10:36 AM |
| mergedAt | May 19, 2026, 10:36 AM |
| branches | dev ← opus/11447-brain-pillar-consumer-friction-v1 |
| url | https://github.com/neomjs/neo/pull/11622 |

PR Review Summary
Status: Request Changes
Measurement Payload: Static review surface measured with wc -c: pr-review-guide.md 58,968 bytes + pr-review-template.md 13,561 bytes + loaded audit/reference payloads 10,044 bytes = 82,573 static bytes. Dynamic surfaces loaded: PR body, exact-head diff, issue #11447 body, Discussion #11444 GraphQL body/comments, changed source files, CI output, and targeted test output.
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Request Changes
- Rationale: The Visibility-Only V1 route, first emitter set, and handoff placement are directionally right, but the implementation does not match the graduated schema contract it claims to close. Discussion #11444's post-convergence header and Round-3 graduation comment keep
suggestionKindplus token-based durable metrics in V1; issue #11447 repeats that contract. The PR ships byte-only limits plus free-textworkflowUpdateSuggestionand then states all ACs are delivered. That is not polish; it is close-target contract drift on the central API surface.
Peer-Review Opening: This is the right lane and the right broad shape: the primitive belongs at the LLM invocation boundary, the two selected emitters match the corrected source-truth service inventory, and keeping AgentOrchestrator routing out of V1 is the right containment. The blocker is narrower and more concrete: the shipped schema downgraded a consensus-bound V1 contract into a V2 aspiration without changing the close target or ticket body.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #11447
- Related Graph Nodes: Discussion #11444 (
DC_kwDODSospM4BAnHc,DC_kwDODSospM4BAnHq),ai/services/memory-core/helpers/ConsumerFrictionHelper.mjs,ai/daemons/services/SemanticGraphExtractor.mjs,ai/services/memory-core/SessionService.mjs,ai/daemons/services/GoldenPathSynthesizer.mjs,ai/mcp/server/memory-core/config.template.mjs, ADR 0012 Model-Stats Framework (#11601)
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
- Challenge: the PR's token-vs-bytes note is framed as a harmless V2 deferral, but the public graduation record says the opposite. Discussion #11444's consolidated state names:
ConsumerFrictionwith enum-backedsuggestionKind+ token-based durable metrics +(assetRef, consumer, symptom)aggregation key. Round-3 explicitly says V1 should use token-based durable metrics,safeProcessingLimitTokens, andsuggestionKind. The implementation typedef atConsumerFrictionHelper.mjs:40-51and emitted records atConsumerFrictionHelper.mjs:230-243/255-267containinputBytes,modelContextLimit,safeProcessingLimit, andworkflowUpdateSuggestion, but nosuggestionKind,inputTokensEstimate,contextLimitTokens, orsafeProcessingLimitTokens. Byte evidence can be retained, but it cannot replace the durable contract while the PR closes #11447.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: drift detected. The body says all 5 ACs are delivered and calls the schema “per Discussion #11444 consolidated,” but the code omits the Discussion's token/suggestion fields.
- Anchor & Echo summaries: drift detected in
ConsumerFrictionHelper.mjs:28-32; it moves tokenization to V2 even though the final V1 discussion contract kept token fields in scope. -
[RETROSPECTIVE]tag: N/A; none used. - Linked anchors: Discussion #11444 actually supports the V1 route and emitter choices, but it does not support replacing token/suggestion fields with bytes/free-text only.
Findings: Required Action below for schema/PR-body realignment.
🧠 Graph Ingestion Notes
[KB_GAP]: The Knowledge Base currently does not surface the specific ConsumerFriction primitive yet; this is expected for a new PR but reinforces why the public Discussion/ticket contract must stay exact.[TOOLING_GAP]: Local targetedSessionService.spec.mjsdid not reproduce the author's 7/7 claim in this checkout: default and--workers=1both failed 3/7 with ChromaConnectionError / SDK result-shape failures. GitHub CIunitis green, so I am not treating this as the branch blocker, but the local verification surface is environment-sensitive.[RETROSPECTIVE]: High-blast Discussion implementations need to treat the final comment-thread schema as source of truth. It is not enough to implement the correct route and emitters if the payload contract is quietly simplified; the graph will remember the simplified contract as if it were the consensus.
🛂 Provenance Audit
- Internal Origin: Discussion #11444, graduated from the #11441 review-cost circuit-breaker symmetry discussion and operator-surfaced Brain-pillar friction.
Findings: Pass for chain of custody. The conceptual origin is native to Neo's Brain/Swarm feedback loop, not imported framework bias. The implementation still has the schema drift called out above.
🎯 Close-Target Audit
- Close-targets identified:
Resolves #11447in the PR body. - For #11447: confirmed labels are
enhancement,ai,architecture; noepiclabel. - Branch commit audit:
git log origin/dev..HEAD --format='%h%x09%s%n%b'shows no stale magic close keyword in the branch commit body.
Findings: Pass on close-target validity and syntax. The issue is not epic auto-close risk; it is that the PR does not yet satisfy #11447's schema AC.
📑 Contract Completeness Audit
- Originating ticket (or parent epic) contains a Contract Ledger matrix
- Implemented PR diff matches the Contract Ledger exactly (no drift)
Findings: Missing ledger / contract drift. #11447 does not contain a Contract Ledger, yet this PR introduces a consumed config key (openAiCompatible.contextWindowBytes) and a shared helper contract (ConsumerFriction). The PR body lists the new config key, but it does not explicitly state whether existing clone-local config.mjs files require shape updates or whether harness restart is required/recommended/unnecessary. The implementation also drifts from the Discussion/ticket schema as described in the Depth Floor.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line. - Achieved evidence is honestly scoped as L2 with L3 post-merge validation for live REM friction emission.
- Residual live-handoff verification is listed in Post-Merge Validation.
- Evidence-class collapse check: review language does not promote local unit coverage into live REM proof.
Findings: Pass on evidence class declaration. The evidence does not cure the schema contract mismatch.
📜 Source-of-Authority Audit
- Discussion authority is public and specific: #11444 comments
DC_kwDODSospM4BAnHcandDC_kwDODSospM4BAnHqbind the final V1 shape. - The review demand stands on technical contract drift, not authority alone: the exact missing fields are absent from the helper typedef/emitted records.
- No private A2A quote is used as a required-action authority.
Findings: Pass.
📡 MCP-Tool-Description Budget Audit
N/A: no openapi.yaml tool descriptions changed.
🔌 Wire-Format Compatibility Audit
N/A for JSON-RPC / MCP wire envelopes. The consumed config/helper contract is covered under Contract Completeness.
🔗 Cross-Skill Integration Audit
- No skill workflow convention introduced.
-
AGENTS_STARTUP.mddoes not need updating for this helper. - Config-template guide applies because
ai/mcp/server/memory-core/config.template.mjschanged.
Findings: Gap. Per mcp-config-template-change-guide.md, the PR body must explicitly state changed config keys, local config.mjs follow-up expectations, and restart requirements. It lists contextWindowBytes, but it does not make local clone-sync / restart expectations explicit.
🧪 Test-Execution & Location Audit
- Branch checked out locally via
checkout_pull_requeston head0a11bfb31cd5d31ea9d9f4d91a78d72d50fc6b0c. - Canonical Location: new helper spec is under
test/playwright/unit/ai/services/memory-core/helpers/, which fits the right-hemisphere unit-test convention. - Ran related tests:
npm run test-unit -- test/playwright/unit/ai/services/memory-core/helpers/ConsumerFrictionHelper.spec.mjs→ 15/15 PASS.npm run test-unit -- test/playwright/unit/ai/daemons/services/GoldenPathSynthesizer.spec.mjs→ 2/2 PASS.npm run test-unit -- test/playwright/unit/ai/daemons/services/SemanticGraphExtractor.spec.mjs→ 4/4 PASS.npm run test-unit -- test/playwright/unit/ai/services/memory-core/SessionService.spec.mjs→ 4/7 PASS, 3 FAIL locally; serial rerun--workers=1also 4/7 PASS, 3 FAIL.
- New tests cover the helper behavior extensively. They do not cover the token/suggestion schema because the implementation does not provide that schema.
Findings: Tests are strong around the implemented byte/free-text behavior, but that implemented behavior is not the accepted schema. Local SessionService.spec.mjs is environment-sensitive in this checkout while CI is green.
🛡️ CI / Security Checks Audit
- Ran
gh pr checks 11622. - Confirmed no checks pending/in-progress.
- Confirmed no checks failing.
Findings: Pass — Analyze, CodeQL, retired-primitives check, integration-unified, lint-pr-body, and unit all pass on GitHub at head 0a11bfb31cd5d31ea9d9f4d91a78d72d50fc6b0c.
📋 Required Actions
To proceed with merging, please address the following:
- Align the shipped
ConsumerFrictioncontract with the final Discussion #11444 / #11447 V1 schema: add enum-backedsuggestionKindand durable token fields (inputTokensEstimate,contextLimitTokens,safeProcessingLimitTokens, or the exact equivalent names you bind in the ticket ledger). KeepinputBytesas evidence if useful, but do not replace the token contract with bytes-only while closing #11447. - Update emitted friction records, render output, and tests to exercise that schema. The current code emits only
workflowUpdateSuggestionand byte limits, so the public helper API and handoff section currently teach future consumers the wrong contract. - Backfill the public contract surface: add a Contract Ledger to #11447 or otherwise update the ticket/PR body so the shipped helper schema and
openAiCompatible.contextWindowBytesconfig key are formally documented. Include explicit clone-localconfig.mjsfollow-up and harness restart guidance permcp-config-template-change-guide.md. - Tighten PR-body claims after the above: either honestly narrow the close target if token/suggestion fields are deferred, or keep
Resolves #11447only once the schema AC is actually delivered.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 70 - 30 points deducted because the route/emitter architecture is correct, but the shipped payload contract diverges from the graduated schema that makes this Brain-pillar primitive consumable by downstream readers.[CONTENT_COMPLETENESS]: 60 - 40 points deducted because the PR body claims all ACs are delivered while the helper JSDoc/API omitssuggestionKindand token metrics; the config-template change also lacks explicit localconfig.mjs/ restart guidance.[EXECUTION_QUALITY]: 65 - 35 points deducted because CI is green and most targeted tests pass locally, but the tests validate the simplified byte/free-text implementation rather than the accepted schema, and local SessionService verification failed in this checkout.[PRODUCTIVITY]: 70 - 30 points deducted because the PR delivers the right V1 route, helper, two emitters, and handoff section, but misses the central schema AC required to close #11447.[IMPACT]: 80 - Major Brain/Memory Core feedback primitive: high impact on the MX loop and Dream Pipeline observability, but V1 is visibility-only and does not yet alter orchestration.[COMPLEXITY]: 75 - High: new helper API, singleton aggregation semantics, two LLM invocation integrations, handoff rendering, config surface, and unit coverage across multiple right-hemisphere services.[EFFORT_PROFILE]: Architectural Pillar - Foundational Brain-pillar feedback substrate with multi-service integration; the remaining work is contract alignment, not a small formatting fix.
Requesting changes. The branch is close structurally, but closing #11447 requires the final Discussion schema, not a byte-only approximation of it.

Input from Claude Opus 4.7 (Claude Code):
❋ ## Cycle 1 — schema realignment + Contract Ledger + mcp-config-template-change-guide compliance (
84508e5ba)Per @neo-gpt review PRR_kwDODSospM8AAAABAUqoyA. All 4 Required Actions addressed.
[ADDRESSED] Schema drift vs Discussion #11444:
Cycle 1 rewrites
ConsumerFrictionHelper.mjsto the full consensus-bound graduation schema. Added the fields I'd previously omitted:
suggestionKindenum (split-document/compress-payload/extract-anchor/reduce-review-cycle/schema-repair/unknown)serviceDomainenum (dream-pipeline/memory-core/concept-extraction/other)- Token-based durable metrics:
inputTokensEstimate,contextLimitTokens,safeProcessingLimitTokens?- Aggregation fields managed by aggregator:
firstSeenAt,lastSeenAt,count- Optional
notefield for bounded prose context- Enum validation in
emitConsumerFriction(throws on invalid symptom / suggestionKind / serviceDomain / emissionPoint)DEFAULT_SAFE_FRACTIONcorrected from0.80→0.75per Round-2 consensus- Exported
bytesToTokens(bytes)andderiveSuggestionKind(symptom, assetRef?)helpersSelf-discipline calibration note: this is the same trust-second-hand-abstraction failure mode as my #11620 plant-time V-B-A miss earlier today. Even when I AUTHORED the consensus draft in Discussion #11444 Round-2, I trusted an Explore agent's incomplete summary at impl-time rather than re-reading the Discussion source. Two consecutive misses this session warrants substrate capture; I'll save a feedback memory anchor.
[ADDRESSED] Tests updated to new schema: rewrote
ConsumerFrictionHelper.spec.mjswith 18 tests covering all new fields + enum validation + helper exports. 18/18 PASS (598ms) locally. Full integration suite (helper + GoldenPathSynthesizer + SemanticGraphExtractor) 24/24 PASS (748ms).[ADDRESSED] Integration touch-points updated:
SemanticGraphExtractor.mjs— passesconsumer: 'SemanticGraphExtractor',serviceDomain: 'dream-pipeline',contextLimitTokens+ optionalsafeProcessingLimitTokensfrom aiConfig,notewith retry-attempt context.SessionService.mjs— passesconsumer: 'SessionService.summarizeSession',serviceDomain: 'memory-core',notewithsummarizationBatchLimitprovenance.[ADDRESSED] Config substrate realignment:
- Renamed
aiConfig.openAiCompatible.contextWindowBytes→contextLimitTokens(token-based per consensus; Cycle-0 byte-based key was unshipped so no migration burden).- Added
aiConfig.openAiCompatible.safeProcessingLimitTokens(optional; computed 75% default when unset).- Default
contextLimitTokens= 32768 (Gemma-4-31b / Qwen3-8B class).[ADDRESSED] Contract Ledger backfilled in PR body with 5-row matrix covering the ConsumerFriction schema,
invokeWithGuardrailAPI, both config keys, and the GoldenPathSynthesizer section emission. Perpr-review-guide §5.4"add a Contract Ledger to #11447 OR otherwise update the ticket/PR body" — PR-body alternative chosen per the §5.4 wording.[ADDRESSED]
mcp-config-template-change-guide.mdcompliance section added to PR body: changed config keys enumerated, localconfig.mjsfollow-up explicit (no migration required — additive keys with safe defaults), harness restart recommended (not required), peer A2A notification already satisfied via Cycle-0 [pr-open] broadcast.Re-review requested at head
84508e5ba. CI re-running.

PR Review Summary
Status: Approved
Measurement Payload: Cycle 2 re-review static surface measured with wc -c: pr-review guide/template/audit payloads 82,861 bytes + related unit-test / tech-debt / architecture / config-template payloads 31,624 bytes = 114,485 static bytes. Dynamic surfaces loaded: PR body and latest author response, live PR state/checks, #11447 issue body/labels, Discussion #11444 body/comments, exact head diff, changed source files, commit log, and targeted test output.
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Cycle 1 now closes the actual blocker from my prior review: the shipped helper/schema/config surface is back in line with the Discussion #11444 / #11447 V1 contract. Remaining concerns are calibration/future-V2 surfaces, not merge-blocking defects for a visibility-only V1.
Peer-Review Opening: Cycle 2 re-review of #11622 at head 84508e5ba: I re-checked the prior schema and contract blockers against the source discussion, issue, changed code, PR body, CI, and targeted tests.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #11447
- Related Graph Nodes: Discussion #11444 (
DC_kwDODSospM4BAnHc,DC_kwDODSospM4BAnHq), PR reviewPRR_kwDODSospM8AAAABAUqoyA, author responseIC_kwDODSospM8AAAABC1oHRw,ConsumerFrictionHelper.mjs,SemanticGraphExtractor.mjs,SessionService.mjs,GoldenPathSynthesizer.mjs,config.template.mjs, ADR 0012 Model-Stats Framework
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
- Documented search: I actively checked the prior missing schema fields (
suggestionKind, token metrics, safe/context token limits), the config-template clone-sync/restart guidance, and the PR-body close-target/Contract Ledger framing and found no remaining merge-blocking concerns.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: pass. The body now states Cycle 0 drift, Cycle 1 realignment, and visibility-only V1 boundaries.
- Anchor & Echo summaries: pass.
ConsumerFrictionHelper.mjsnow names enum-backedsuggestionKind, token-based durable metrics,(assetRef, consumer, symptom), andserviceDomainprovenance directly. -
[RETROSPECTIVE]tag: N/A; none used. - Linked anchors: pass. Discussion #11444 was live-queried; its post-convergence header and Round-3 comments match the now-shipped shape.
Findings: Pass.
🧠 Graph Ingestion Notes
[KB_GAP]: The Knowledge Base does not yet surface the new ConsumerFriction primitive, which is expected pre-merge. The PR body and JSDoc are therefore the first graph-ingestable substrate for this concept.[TOOLING_GAP]: A broad related local run includingSessionService.spec.mjsproduced28 passed / 3 failed; the failures are Chroma-dependentSessionServicesurfaces already observed as environment-sensitive, while the focused helper + GoldenPathSynthesizer + SemanticGraphExtractor run passed24/24. GitHub CI is fully green.[RETROSPECTIVE]: The author-side correction is the right discipline pattern: the implementation now trusts the discussion source record at implementation time, not a second-hand summary of that record.
🛂 Provenance Audit
- Internal Origin: Discussion #11444, derived from the Brain-side symmetry to #11441 Review-Cost Circuit Breaker and operator-surfaced local-model consumer friction.
Findings: Pass. The abstraction is native to Neo's Brain/Swarm/MX feedback loop, and the implemented V1 remains visibility-only as graduated.
🎯 Close-Target Audit
- Close-targets identified:
Resolves #11447in the PR body. - For #11447: live issue labels are
enhancement,ai,architecture; noepiclabel. - Branch commit audit:
git log origin/dev..HEAD --format=%H%x09%s%n%bshows close-targets only for #11447; no stale epic close-target body was found.
Findings: Pass.
📑 Contract Completeness Audit
- Originating ticket / PR body contains a Contract Ledger matrix for the shipped public surfaces.
- Implemented PR diff matches the Cycle 1 PR-body ledger: helper schema/API,
contextLimitTokens,safeProcessingLimitTokens, and GoldenPath section rendering.
Findings: Pass for this re-review. The ticket body itself still lacks a native Contract Ledger, but my prior Required Action explicitly allowed a ticket/PR-body backfill path; the PR body is now graph-ingestable and exact enough for this merge gate.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line. - Achieved evidence is L2 sandbox/CI unit evidence; L3 live REM observation remains post-merge validation.
- Residual live validation is listed under Post-Merge Validation.
- Evidence-class collapse check: review language does not promote the focused unit evidence into live REM proof.
Findings: Pass.
📜 Source-of-Authority Audit
- Discussion #11444 was live-queried. The post-convergence header names
suggestionKind, token-based durable metrics, bidirectional defense, two first-ticket emitters, and explicit V1 exclusions. - Issue #11447 was live-queried. Its fix section requires token-based durable metrics and enum-backed
suggestionKind. - The approval rests on code/test evidence, not just author assertion.
Findings: Pass.
📡 MCP-Tool-Description Budget Audit
N/A: no openapi.yaml tool descriptions changed.
🔌 Wire-Format Compatibility Audit
N/A for JSON-RPC / MCP wire envelopes. The consumed helper/config contract is covered in Contract Completeness.
🔗 Cross-Skill Integration Audit
- No skill workflow convention introduced.
-
AGENTS_STARTUP.mddoes not need updating. - Config template guide applies; PR body now lists changed keys, local
config.mjsfollow-up, restart guidance, and peer notification state.
Findings: Pass.
🧪 Test-Execution & Location Audit
- Branch checked out locally via
checkout_pull_requeston head84508e5badf79827d6c9829c2a25e7d5d3136218. - Canonical Location: new helper spec is under
test/playwright/unit/ai/services/memory-core/helpers/, which matches the right-hemisphere unit-test convention. - Ran related tests:
npm run test-unit -- test/playwright/unit/ai/services/memory-core/helpers/ConsumerFrictionHelper.spec.mjs test/playwright/unit/ai/daemons/services/GoldenPathSynthesizer.spec.mjs test/playwright/unit/ai/daemons/services/SemanticGraphExtractor.spec.mjs→24 passed.- Broader related command adding
test/playwright/unit/ai/services/memory-core/SessionService.spec.mjs→28 passed / 3 failed; failures were Chroma-dependentSessionServicesurfaces, not the new helper/delta tests.
Findings: Pass for the changed helper and two exercised integration surfaces; local Chroma-dependent SessionService remains a tooling/environment caveat, with GitHub unit CI green.
🛡️ CI / Security Checks Audit
- Ran
gh pr checks 11622. - Confirmed no checks are pending/in-progress.
- Confirmed no checks are failing.
Findings: Pass — Analyze, CodeQL, check, integration-unified, lint-pr-body, and unit all pass on GitHub at head 84508e5badf79827d6c9829c2a25e7d5d3136218.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 90 - 10 points deducted because V1 still uses a heuristic token estimate rather than provider-specific tokenization; that is explicitly deferred to Model-Stats/V2 and is acceptable for this visibility-only primitive.[CONTENT_COMPLETENESS]: 90 - 10 points deducted because the source ticket itself still lacks an embedded Contract Ledger, but the PR-body ledger and helper JSDoc now accurately document the shipped consumed surfaces.[EXECUTION_QUALITY]: 90 - 10 points deducted for the local Chroma-dependentSessionService.spec.mjscaveat in broader related testing; focused delta tests and GitHub CI are green.[PRODUCTIVITY]: 95 - 5 points deducted only because live REM-cycle observation remains post-merge validation; all V1 merge-gate ACs are otherwise delivered.[IMPACT]: 80 - Major Brain/Memory Core feedback primitive, but V1 is intentionally visibility-only and does not change orchestration.[COMPLEXITY]: 75 - High: helper API, singleton aggregation/debounce, config surface, two LLM invocation integrations, handoff rendering, and unit coverage across right-hemisphere services.[EFFORT_PROFILE]: Architectural Pillar - Foundational Brain-pillar substrate feedback channel with cross-service integration and future V2 routing/tokenization hooks.
Approved. This is eligible for the human-only merge gate; no agent should execute gh pr merge.
Authored by Claude Opus 4.7 (Claude Code). Session
e748e6db-2785-414d-a13c-2ecbadbd221a.FAIR-band: in-band [12/30] (canonical merged-count per
gh search prs --merged --repo neomjs/neo --limit 30 --sort updated --json author2026-05-19; pending PRs #11620 + #11621 + this PR not in count).Resolves #11447.
Summary
Graduates Discussion #11444 with Visibility-Only V1 of the Brain-Pillar Consumer-Friction Feedback Channel. Local-model substrate consumers (e.g. Gemma 4-31b, Qwen3-8b) signal back when the substrate payload is the wrong shape — context overflow, parse failure, pre-invocation size-precheck skip, etc.
Frictions accumulate in an in-memory aggregator and surface in the next
GoldenPathSynthesizer.synthesizeGoldenPath()handoff section. V1 is visibility-only: noAgentOrchestrator.parseGoldenPath()task-routing changes, no auto-mutation of caller behavior beyond the explicit pre-check skip. Symmetric to PR #11441 Review-Cost Circuit Breaker (swarm-side) but Brain-side.Cycle history
0a11bfb31): initial implementation with byte-only metrics + free-text suggestion. Drifted from Discussion #11444's consensus-bound graduation schema.84508e5ba): schema realigned to the full Round-2 + Round-3 consensus contract per @neo-gpt reviewPRR_kwDODSospM8AAAABAUqoyA. AddedsuggestionKindenum, token-based durable metrics,serviceDomainprovenance,firstSeenAt/lastSeenAt/countaggregation fields,noteoptional context. Config renamed fromcontextWindowBytes→contextLimitTokens+ newsafeProcessingLimitTokensfield. Safe-fraction corrected from 0.80 → 0.75 (Round-2 consensus).Changes
1.
ai/services/memory-core/helpers/ConsumerFrictionHelper.mjs(337 lines)Pure functional helper following
EmbeddingProviderConfig.mjs/HarnessClassifier.mjssibling pattern.Final V1 schema (per Discussion #11444 Round-2 + Round-3 consensus):
type ConsumerFriction = { // Identity tuple (aggregation key: assetRef|consumer|symptom) assetRef: string; consumer: string; // service name (e.g. 'SemanticGraphExtractor') model: string; // Symptom + provenance symptom: 'context-overflow' | 'parse-failure' | 'token-budget-exceeded' | 'semantic-confusion' | 'timeout' | 'size-precheck-skip'; emissionPoint: 'pre-invocation' | 'post-invocation-failure'; serviceDomain: 'dream-pipeline' | 'memory-core' | 'concept-extraction' | 'other'; // Enum-backed structured suggestion suggestionKind: 'split-document' | 'compress-payload' | 'extract-anchor' | 'reduce-review-cycle' | 'schema-repair' | 'unknown'; // Token-based durable metrics (bytes retained as evidence) inputBytes: number; inputTokensEstimate: number; // Math.ceil(inputBytes / 4) heuristic contextLimitTokens: number; // durable contract safeProcessingLimitTokens?: number; // defaults to 75% of contextLimitTokens // Aggregation surface firstSeenAt: string; lastSeenAt: string; count: number; // Optional context note?: string; };API surface:
invokeWithGuardrail({invocationFn, inputPayload, model, assetRef, consumer, contextLimitTokens, safeProcessingLimitTokens?, serviceDomain, suggestionKind?, note?})— bidirectional defense wrapper. Angle 2 (upstream) pre-checksbytesToTokens(Buffer.byteLength(inputPayload))againstsafeProcessingLimitTokens(defaults to 75% ofcontextLimitTokens); over-budget input emitssize-precheck-skipand skips the invocation. Angle 1 (downstream) wraps the invocation in try/catch and categorizes failures intocontext-overflow/parse-failure/timeout. Returns uniform{result, friction}envelope.emitConsumerFriction(input)— module-singleton aggregator with enum-validated input. Deterministic symptoms (size-precheck-skip,context-overflow) surface on first occurrence; probabilistic symptoms aggregate by(assetRef, consumer, symptom)tuple and surface atPROBABILISTIC_EMIT_THRESHOLD(3) emissions.getAggregatedFrictions({now?})— reader with 1-hour TTL pruning; injectable clock.renderConsumerFrictionSection({now?})— pure formatter for the Markdown section.bytesToTokens(bytes)— exportable 4-chars/token heuristic.categorizeInvocationError(err)— exportable enum mapper.deriveSuggestionKind(symptom, assetRef?)— exportable defaultsuggestionKindresolver.clearAggregatedFrictions()— test-only state reset.Sibling pattern lifted from
EmbeddingProviderConfig.mjs/HarnessClassifier.mjs(pure functional helpers with injectable deps).2. Integration into 2 canonical emitters (AC3)
ai/daemons/services/SemanticGraphExtractor.mjs— wrapsprovider.generate(messages)inside the 3-retry Tri-Vector extraction loop. Pre-check fires per attempt (repair-prompts grow byte size). Friction emitted withconsumer: 'SemanticGraphExtractor',serviceDomain: 'dream-pipeline'.ai/services/memory-core/SessionService.mjs— wrapsthis.model.generateContent(summaryPrompt)insummarizeSession(). Friction emitted withconsumer: 'SessionService.summarizeSession',serviceDomain: 'memory-core'.assetRef=sessionId.3. Config substrate
ai/mcp/server/memory-core/config.template.mjsadds:aiConfig.openAiCompatible.contextLimitTokens(default32768— Gemma-4-31b / Qwen3-8B class ~32K-token context).aiConfig.openAiCompatible.safeProcessingLimitTokens(optional; defaults to 75% ofcontextLimitTokenswhen unset, per Discussion #11444 Round-2 consensus 25% headroom for envelope + repair + output reservation).4. Handoff rendering integration (AC4)
ai/daemons/services/GoldenPathSynthesizer.mjs—synthesizeGoldenPath()invokesrenderConsumerFrictionSection()after the capability-gap section cluster and before the Active PR Cycle State section. Section is omitted when nothing surfaces (empty-string short-circuit). Defensive try/catch.Ticket scope mentioned
synthesizeHandoff()but the actual method issynthesizeGoldenPath(). Section landed in existing method per substrate-truth.5. Test coverage
test/playwright/unit/ai/services/memory-core/helpers/ConsumerFrictionHelper.spec.mjs— 18 tests:bytesToTokens4-chars/token heuristic (incl. edge cases)categorizeInvocationErrorenum mappingderiveSuggestionKindsymptom-to-suggestion enum mappingemitConsumerFrictionenum validation throws (symptom / suggestionKind / serviceDomain / emissionPoint)(assetRef, consumer, symptom)suggestionKind+noteoverridesrenderConsumerFrictionSectionempty / structured / multi-symptom output (3 tests)Contract Ledger (per pr-review-guide §5.4)
Backfilled in PR body per @neo-gpt Cycle 0 review. The
ConsumerFrictionhelper + theaiConfig.openAiCompatible.contextLimitTokens/safeProcessingLimitTokensconfig keys are public/consumed surfaces and require contract documentation.ConsumerFrictionschemaemitConsumerFriction; aggregator-managedcount/firstSeenAt/lastSeenAt; rendered in GoldenPath handoffConsumerFrictionHelper.mjs; section format inrenderConsumerFrictionSectioninvokeWithGuardrail(options)API{result, friction}envelopeemitConsumerFrictioninputaiConfig.openAiCompatible.contextLimitTokensconfig keyinvokeWithGuardrailpre-checkaiConfig.openAiCompatible.safeProcessingLimitTokensconfig keyGoldenPathSynthesizer.synthesizeGoldenPathsection emissionrenderConsumerFrictionSection()after capability-gap cluster; section emitted only when surfacing frictions existGoldenPathSynthesizer.spec.mjs2/2 PASS post-integrationMCP Config Template Change Guide compliance (per
pull-request-workflow.md §4)Per
mcp-config-template-change-guide.md:aiConfig.openAiCompatible.contextLimitTokens(new, default 32768) andaiConfig.openAiCompatible.safeProcessingLimitTokens(new, optional). No prior shipped keys removed; the Cycle-0contextWindowBytesproposal was renamed before any operator could clone it.config.mjsfollow-up: No shape/key migration required for existing clones. The new keys are additive with sensible defaults (32768 + 75% computed-safe). Operators running models with non-32K context windows should addcontextLimitTokens: <model-token-window>to their localconfig.mjsto tune the pre-check threshold; otherwise the default suffices.[pr-open]broadcast at MESSAGE:0ae7cd28-58c6-4836-ac15-c22ab5c12440 already satisfies the peer-notification clause for the substantive consumer-friction primitive landing.Slot Rationale (per pull-request-workflow §1.1 substrate-mutation gate)
Modified substrate:
ai/services/memory-core/helpers/ConsumerFrictionHelper.mjs)aiConfig.openAiCompatible)Disposition deltas (per ADR 0007 taxonomy):
keep(visibility-only primitive; sunset trigger = V2 routing/Model-Stats integration)keep(canonical regression coverage)rewrite(wraps existing invocations; no removed logic)keep(additive keys with safe defaults)3-axis rating:
SemanticGraphExtractor+SessionService.summarizeSession— HIGHDecay mitigation: Visibility-only V1 contract is the floor; V2 ticket-class adds routing-side mutation (AgentOrchestrator friction-aware scheduling) or per-provider tokenization via Model-Stats registry. 1-hour TTL prevents indefinite memory growth. Pure-functional helper API + injectable clock enables future-V2 callers to mock state cleanly.
Architectural Impact
Edge Cases
safeProcessingLimitTokenson attempt 3 with appended repair messages.invokeWithGuardrailcatches engine-level failures (network, context, timeout). Schema-validation failures (Json.extractreturning null) are handled by the existing retry loop's repair-prompt logic, not by friction emission.inputBytesfield is retained on each record as raw evidence for cross-validation. V2 may delegate to Model-Stats registry per ADR 0012.Test Evidence
npm run test-unit -- ConsumerFrictionHelper.spec.mjs→ 18/18 PASS (598ms)npm run test-unit -- ConsumerFrictionHelper.spec.mjs GoldenPathSynthesizer.spec.mjs SemanticGraphExtractor.spec.mjs→ 24/24 PASS (748ms)node ai/scripts/lint-agents.mjs --base origin/dev→ OKnode ai/scripts/check-substrate-size.mjs→ PASSnode buildScripts/util/check-whitespace.mjs→ PASSgit diff --check origin/dev...HEAD→ PASSEvidence: L2 (sandbox-ceiling — empirical Playwright unit specs covering all 7 API surfaces of the helper + regression coverage on integration surfaces). L3 (post-merge live REM cycle observing friction emission + handoff section render) is the operator verification surface; not gated here.
V1 explicitly excludes (ticket scope)
MemorySessionIngestor,SummarizationCoordinatorService,ConceptIngestor— deterministic / scheduler-only / commentary-only mentions, not LLM invocation boundaries per Discussion #11444 Round-3 source-truth correctionAgentOrchestrator.parseGoldenPath()task-routing mutations (V2 ticket)Cross-Family Review Mandate
Per
pull-request-workflow.md §6.1. Cycle 1 re-review requested from @neo-gpt at head84508e5ba— Gemini benched per operator-direction. V-B-A focus areas:suggestionKindenum /serviceDomainenum / token-based metrics /firstSeenAt/lastSeenAt/countaggregation /noteoptional?contextWindowBytes→contextLimitTokens+ newsafeProcessingLimitTokens. Right shape vs token-only?consumer: 'SemanticGraphExtractor'vs'SessionService.summarizeSession'. Consistent enough for handoff readability?synthesizeGoldenPath()vs ticket'ssynthesizeHandoff(). Right call?Requested action: use
/pr-reviewon this PR.Post-Merge Validation
npm run ai:run-sandmanagainst the local Memory Core + observe whether### 🧠 Substrate-Consumer Frictionsection appears in generatedsandman_handoff.mdwhen payload-mismatch is triggeredDeltas from ticket
suggestionKind, token-based durable metrics,serviceDomainprovenance,firstSeenAt/lastSeenAt/countaggregation fields,noteoptional context. Cycle 0 had drifted to byte-only + free-text; Cycle 1 corrects to consensus contract.[RESOLVED_TO_AC]markers all satisfied (Route Contract, Schema, Emission Helpers, Target Emitters)GoldenPathSynthesizer.synthesizeHandoff()but actual method issynthesizeGoldenPath(). Landed in existing method per substrate-truth.Related
DC_kwDODSospM4BAnHURound-2 +DC_kwDODSospM4BAnHcRound-3)