/**
* insight-layer-d.js
*
* Layer D – Insight Review
* ————————
*
* Purpose:
* Provide an insightful closing reflection on where the user is with,
* and what they got from, the completed Insight.
*
* Layer D must NOT:
* – continue the live coaching conversation
* – decide next framework steps
* – extract new evidence from a single turn
* – exaggerate progress
* – invent development
*/
(function () {
‘use strict’;
function clean(value) {
return String(value == null ? ” : value).trim();
}
/**
* D1 – Review Eligibility Assessment
*/
async function assessReviewEligibility(ctx) {
const config = ctx.config || {};
if (!config.aiUrl) {
return {
review_required: false,
development_summary: ”,
reason: ‘No AI endpoint available.’
};
}
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are D1 Review Eligibility Assessment.
Your role:
Decide whether the completed Insight conversation contains enough meaningful material for an insightful closing reflection on where the user is with this Insight.
Return ONLY valid JSON:
{
“review_required”: true|false,
“insight_position”: “…”,
“reason”: “…”
}
Rules:
– Base this only on the completed Insight conversation.
– Look for a meaningful view, understanding, experience, tension, realisation or conclusion about the Insight.
– The user does not need to have changed or developed for a meaningful review to be possible.
– Establish whether the user’s position was already present at the start, became clearer through exploration, or genuinely changed during the conversation.
– Treat a position as changed or developed only when the conversation provides evidence of a difference between an earlier and later position.
– Exploring, explaining, exemplifying or applying an existing belief does not by itself mean that belief developed or changed.
– Do not reward length alone.
– Set review_required to false only when there is too little meaningful material to say anything useful about where the user is with this Insight.
– Do not write the user-facing review.`
},
{
role: ‘user’,
content:
`COMPLETE INSIGHT CONVERSATION:
${JSON.stringify(ctx.thread || [])}
REFLECTIONS GENERATED:
${JSON.stringify(ctx.reflectionHistory || [])}
TRAIT UPDATE RECOMMENDATIONS:
${JSON.stringify(ctx.traitUpdates || [])}
PREVIOUS INSIGHT REVIEWS:
${JSON.stringify(ctx.previousInsightReviews || [])}
RELEVANT TRAIT HISTORY:
${JSON.stringify(ctx.relevantTraitHistory || [])}
INSIGHT SUMMARY:
${clean(ctx.insightSummary)}`
}
]
};
let parsed = {
review_required: false,
insight_position: ”,
reason: ”
};
try {
const aiResult = await window.cpAiService.callJson({
task: ‘layer_d_review_eligibility’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer D1 AI request failed.’
);
}
parsed = aiResult.data || parsed;
} catch (err) {
console.log(‘LAYER D1 ERROR:’, err);
}
return {
review_required: !!parsed.review_required,
insight_position: clean(parsed.insight_position),
reason: clean(parsed.reason)
};
}
/**
* D2 – Review Evidence Selection
*/
async function selectReviewEvidence(ctx, eligibility) {
const config = ctx.config || {};
if (!eligibility.review_required || !config.aiUrl) {
return { selected_evidence: [] };
}
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are D2 Closing Reflection Observation.
Your role:
Consider the completed Insight conversation as a whole and identify the single most useful observation that could form the basis of the user’s closing reflection.
Ask yourself:
“What seems most significant about where this user is with this Insight after exploring it?”
The primary observation may concern:
– a view or understanding the user brought to the Insight;
– something that became clearer through exploring it;
– a meaningful connection between things the user said;
– a tension that remains unresolved;
– or something personally significant that became apparent through a concrete experience or example.
You may also identify ONE qualifying observation, but only when it materially changes, complicates, qualifies or provides necessary context for the primary observation.
Return ONLY valid JSON:
{
“primary_observation”: “…”,
“primary_supporting_evidence”: [
{
“evidence”: “…”,
“quote”: “…”
}
],
“qualifying_observation”: “”,
“qualifying_supporting_evidence”: [],
“reason”: “…”
}
Rules:
– Consider the completed Insight conversation as a whole before choosing the primary observation.
– Choose one primary observation that best captures where this Insight left the user.
– Prefer an observation that says something genuinely useful about the user’s relationship with this Insight, rather than merely restating one of their statements.
– A qualifying observation is optional and there may be no qualifying observation at all.
– Select at most one qualifying observation.
– Select a qualifying observation only when it materially changes, complicates, qualifies or provides necessary context for the primary observation.
– Do not select a qualifying observation merely because it is interesting, significant in isolation, or another thing that could be mentioned.
– The primary and qualifying observations must be capable of forming one coherent closing reflection rather than two separate discoveries.
– Material raised or revisited near the end of the conversation must be considered on the same basis as material from the framework exploration. Its position in the conversation must neither guarantee nor prevent its selection.
– Concrete experiences may reveal something more personally significant than abstract statements. Use your judgement rather than automatically preferring either.
– An observation may draw together different parts of the conversation only where the connection is directly supported by what the user said.
– Distinguish what the user already believed from anything that genuinely emerged, became clearer or changed during the conversation.
– Treat something as development or change only when the conversation provides evidence of a difference between the user’s earlier and later position.
– Exploring, explaining, exemplifying or applying an existing belief may deepen the conversation without changing that belief.
– Do not claim that the Insight caused something unless the conversation clearly establishes that.
– Supporting evidence must come directly from the completed Insight conversation.
– Include only the evidence needed to support each selected observation.
– Do not analyse the user’s overall personality or long-term development.
– Do not write the user-facing review.`
},
{
role: ‘user’,
content:
`D1 ELIGIBILITY:
${JSON.stringify(eligibility)}
COMPLETE INSIGHT CONVERSATION:
${JSON.stringify(ctx.thread || [])}
REFLECTION HISTORY:
${JSON.stringify(ctx.reflectionHistory || [])}
TRAIT UPDATE RECOMMENDATIONS:
${JSON.stringify(ctx.traitUpdates || [])}`
}
]
};
let parsed = {
primary_observation: ”,
primary_supporting_evidence: [],
qualifying_observation: ”,
qualifying_supporting_evidence: [],
reason: ”
};
try {
const aiResult = await window.cpAiService.callJson({
task: ‘layer_d_review_evidence’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer D2 AI request failed.’
);
}
parsed = aiResult.data || parsed;
} catch (err) {
console.log(‘LAYER D2 ERROR:’, err);
}
return {
primary_observation: clean(parsed.primary_observation),
primary_supporting_evidence: Array.isArray(parsed.primary_supporting_evidence)
? parsed.primary_supporting_evidence
: [],
qualifying_observation: clean(parsed.qualifying_observation),
qualifying_supporting_evidence: Array.isArray(parsed.qualifying_supporting_evidence)
? parsed.qualifying_supporting_evidence
: [],
reason: clean(parsed.reason)
};
}
/**
* D3 – Insight Review Writer
*/
async function writeInsightReview(ctx, eligibility, selectedEvidence) {
const config = ctx.config || {};
const fallback = clean(eligibility.insight_position) ||
‘You explored this Insight and shared your perspective on it.’;
if (!config.aiUrl) return fallback;
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are D3 Insight Review Writer.
Before the review, thank the user for completing this Insight conversation.
Then introduce the review naturally with one short sentence such as:
“Thank you for exploring this Insight. Before we move on, here’s a brief reflection on what seemed most significant.”
Keep this introduction warm, brief and conversational.
Write the user-facing Insight Review.
The purpose of the review is to write a closing reflection for this Insight.
Imagine you are bringing a coaching conversation to a natural close.
Do not try to summarise the conversation.
Instead, leave the user with the single most meaningful understanding, conclusion or unresolved tension that emerged through exploring this Insight.
The review should complete this Insight. It should not attempt to explain the user’s overall personality or long-term development.
The review will also become part of the AI Coach’s long-term coaching record, so every observation must be accurate enough to rely on in future conversations.
The supplied material contains one primary observation and may also contain one qualifying observation.
The review must remain one coherent closing reflection. It must not become a summary or a list of discoveries.
Rules:
– Focus only on the current Insight.
– Build the reflection around the supplied primary observation.
– Every claim in the primary observation must be supported by its supplied evidence.
– If a qualifying observation has been supplied, incorporate it only where it materially changes, complicates, qualifies or provides necessary context for the primary observation.
– The qualifying observation must be woven naturally into the same coherent reflection, not presented as a separate discovery or second takeaway.
– Every claim in the qualifying observation must be supported by its supplied evidence.
– If no qualifying observation has been supplied, do not invent one.
– Do not invent facts, progress or development.
– Do not strengthen, connect or explain the evidence beyond what it supports.
– If either supplied observation goes beyond its supporting evidence, weaken or omit the unsupported part rather than repeating it.
– Aim to say something useful about the user’s relationship with this Insight, rather than simply restating what they said.
– Draw together selected evidence only where the connection is directly supported by the conversation.
– Never attribute an existing belief, preference or approach to this conversation unless the evidence clearly shows that it emerged or changed here.
– Distinguish carefully between:
• what the user already believed;
• what the user described during this Insight;
• what became clearer during this Insight;
• and what genuinely changed during this conversation.
– Do not summarise the conversation.
– Do not list multiple discoveries.
– Produce one coherent reflection centred on the primary observation, incorporating the qualifying observation only when supplied and necessary.
– Every sentence should contribute to that same coherent reflection.
– Do not analyse the user’s personality or long-term development.
– Where uncertainty or tension remains, acknowledge it naturally rather than implying resolution.
– Do not introduce new coaching themes.
– Do not give advice.
– Do not ask questions.
– Do not encourage or motivate the user.
– Write directly to the user in a calm, natural coaching voice.
– The review should feel like a coach bringing a conversation to a close, not like a report or assessment.
– Keep the reflection to no more than three sentences (approximately 40–60 words).
– It should feel like something a thoughtful coach would naturally say at the end of a conversation, not like a written report.
– Use UK English throughout, including UK spelling (for example: prioritise, recognise, behaviour, organisation), not US spelling.
Before writing each sentence, ask yourself:
“Could the user reasonably reply, ‘I never said that’?”
If the answer might be yes, rewrite or remove the sentence.
Write only what the selected evidence supports.`
},
{
role: ‘user’,
content:
`PRIMARY CLOSING OBSERVATION:
${clean(selectedEvidence.primary_observation)}
PRIMARY SUPPORTING EVIDENCE:
${JSON.stringify(selectedEvidence.primary_supporting_evidence || [])}
QUALIFYING OBSERVATION:
${clean(selectedEvidence.qualifying_observation)}
QUALIFYING SUPPORTING EVIDENCE:
${JSON.stringify(selectedEvidence.qualifying_supporting_evidence || [])}
INSIGHT POSITION:
${clean(eligibility.insight_position)}
PREVIOUS INSIGHT REVIEWS:
${JSON.stringify(ctx.previousInsightReviews || [])}
RELEVANT TRAIT HISTORY:
${JSON.stringify(ctx.relevantTraitHistory || [])}
INSIGHT SUMMARY:
${clean(ctx.insightSummary)}`
}
]
};
try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_d_review_writer’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer D3 AI request failed.’
);
}
return clean(aiResult.data) || fallback;
} catch (err) {
console.log(‘LAYER D3 ERROR:’, err);
return fallback;
}
}
/**
* D4 – Minimal Closing Review
*/
async function writeMinimalClosingReview(ctx, eligibility) {
const config = ctx.config || {};
const fallback =
“Thank you for exploring this Insight. Before we move on, here’s a brief reflection on where the conversation left things. You shared your perspective on the Insight, without a significant new development emerging during the conversation.”;
if (!config.aiUrl) return fallback;
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are D4 Minimal Closing Review Writer.
This is the closing reflection for a completed Insight conversation.
Begin exactly with:
“Thank you for exploring this Insight. Before we move on, here’s a brief reflection on where the conversation left things.”
Then write a brief factual reflection on where the user is with this Insight.
Rules:
– Make it unmistakable that this is the closing reflection for the Insight.
– After the fixed introduction, write only 1–2 concise sentences.
– Stay grounded in what the user actually said during this Insight.
– Acknowledge their position or perspective where useful.
– Do not imply significant development, discovery, progress or change if the evidence does not support it.
– Do not claim that an existing belief became clearer merely because the user repeated or explained it.
– Do not manufacture significance because the conversation is ending.
– Do not sound disappointed, corrective or apologetic.
– Do not comment negatively on the quality of the conversation.
– Do not say that the user failed to provide new insights or experiences.
– Do not ask questions.
– Do not encourage further reflection.
– Do not give advice.
– Keep the complete response, including the introduction, to no more than 3 sentences.
– Use UK English throughout, including UK spelling (for example: prioritise, recognise, behaviour, organisation), not US spelling.`
},
{
role: ‘user’,
content:
`D1 ELIGIBILITY:
${JSON.stringify(eligibility)}
INSIGHT SUMMARY:
${clean(ctx.insightSummary)}
COMPLETE INSIGHT CONVERSATION:
${JSON.stringify(ctx.thread || [])}`
}
]
};
try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_d_minimal_review’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer D4 AI request failed.’
);
}
return clean(aiResult.data) || fallback;
} catch (err) {
console.log(‘LAYER D4 ERROR:’, err);
return fallback;
}
}
/**
* runLayerD()
*/
async function runLayerD(ctx) {
const eligibility = await assessReviewEligibility(ctx);
if (!eligibility.review_required) {
const review = await writeMinimalClosingReview(ctx, eligibility);
return {
review_required: false,
eligibility,
selected_evidence: [],
review,
review_type: ‘minimal’,
debug: {
layer: ‘D’,
decision: ‘D1’,
value: ‘minimal_review’,
reason: eligibility.reason
}
};
}
const selectedEvidence = await selectReviewEvidence(ctx, eligibility);
const review = await writeInsightReview(ctx, eligibility, selectedEvidence);
return {
review_required: true,
eligibility,
closing_observation: selectedEvidence.primary_observation || ”,
selected_evidence: selectedEvidence.primary_supporting_evidence || [],
qualifying_observation: selectedEvidence.qualifying_observation || ”,
qualifying_evidence: selectedEvidence.qualifying_supporting_evidence || [],
review,
review_type: ‘full’,
debug: {
layer: ‘D’,
decision: ‘D3’,
value: ‘full_review’,
reason: eligibility.reason,
details: [
{
decision: ‘D2’,
value: `${(selectedEvidence.primary_supporting_evidence || []).length} primary supporting evidence item(s)` +
(selectedEvidence.qualifying_observation
? ` + ${(selectedEvidence.qualifying_supporting_evidence || []).length} qualifying supporting evidence item(s)`
: ”),
reason: selectedEvidence.reason || ”
}
]
}
};
}
window.cpInsightLayerD = {
runLayerD,
assessReviewEligibility,
selectReviewEvidence,
writeInsightReview,
writeMinimalClosingReview
};
})();