/**
* insight-layer-c.js
*
* Layer C – Response Writing
* ————————–
*
* Purpose:
* Express Layer B decisions as user-facing coaching responses.
*
* Layer C must NOT:
* – decide whether to reflect
* – decide whether to move step
* – decide whether to follow up
* – assess traits
* – extract evidence
* – write Insight reviews
* – save directly to the database
*/
(function () {
‘use strict’;
function clean(value) {
return String(value == null ? ” : value).trim();
}
/**
* C1 – Reflection Writer
*
* Writes a short reflection only when Layer B has already decided
* that a reflection is required.
*/
async function writeReflection(ctx, layerB) {
const config = ctx.config || {};
const reflection = layerB?.reflection || {};
if (!reflection.reflection_required) return ”;
if (!config.aiUrl) return clean(reflection.reflection_basis);
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are C1 Reflection Writer in a structured AI Coach system.
Your role:
Write a brief user-facing reflection based only on Layer B’s reflection basis.
Rules:
– Write 1–3 sentences.
– Stay close to the user’s evidence.
– Do not infer hidden motives.
– Do not diagnose.
– Do not praise.
– Do not introduce a new coaching direction.
– Do not ask a question.
– Use UK English throughout, including UK spelling (for example: prioritise, recognise, behaviour, organisation), not US spelling.`
},
{
role: ‘user’,
content:
`REFLECTION BASIS:
${clean(reflection.reflection_basis)}
RELEVANT EVIDENCE:
${JSON.stringify(reflection.relevant_evidence || [])}
LATEST USER REPLY:
${clean(ctx.userText)}`
}
]
};
try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_c_reflection_writer’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer C1 AI request failed.’
);
}
return clean(aiResult.data) ||
clean(reflection.reflection_basis);
} catch (err) {
console.log(‘LAYER C1 ERROR:’, err);
return clean(reflection.reflection_basis);
}
}
/**
* C2 – Question Writer
*
* Writes the next coaching question according to Layer B’s response strategy.
*
* Layer B decides:
* – next_step
* – follow_up
* – non_resonance
*
* Layer C only writes.
*/
async function writeQuestion(ctx, layerB, reflectionText) {
const config = ctx.config || {};
const strategy = layerB?.strategy || {};
const responseStrategy = clean(strategy.response_strategy);
if (!config.aiUrl) {
if (responseStrategy === ‘initial_integration’) {
return clean(ctx.insightQuestion);
}
return clean(
ctx.nextFrameworkStep ||
ctx.currentFrameworkStep ||
ctx.insightQuestion
);
}
const targetStep =
responseStrategy === ‘next_step’
? clean(ctx.nextFrameworkStep || ctx.currentFrameworkStep)
: clean(ctx.currentFrameworkStep);
console.log(‘LAYER C TEST 20260831-A — EDITED FILE IS RUNNING’, {
responseStrategy,
currentFrameworkStep: clean(ctx.currentFrameworkStep),
nextFrameworkStep: clean(ctx.nextFrameworkStep),
targetStep,
layerBEvidence: ctx.layerBEvidence || []
});
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are C2 Coaching Response Writer in a structured AI Coach system.
Your role:
Write the next user-facing coaching response so that it feels like a natural continuation of the conversation while following the coaching direction already selected by the architecture.
The response must contain one coaching question.
Where the instructions below call for acknowledgement, recognition or a transition, this may appear briefly before the question or be incorporated naturally into the question itself.
The response may therefore consist of:
1. Any reflection already written by C1 (if present).
2. Where appropriate, a brief acknowledgement, recognition or natural transition responding to the user’s latest contribution.
3. One coaching question.
Do not rewrite, replace or duplicate the reflection.
Your responsibility is to express the coaching move selected by the architecture as a coherent conversational response. You do not decide whether to follow up or move forward; Layer B has already made that decision.
If the response strategy is initial, this is the first coaching question of the Insight.
In this situation:
– there is no latest user reply in this Insight yet;
– use the supplied TARGET STEP COVERAGE together with OPENING ESTABLISHED MATERIAL and OPENING REMAINING GAP to determine how to write the opening question;
– OPENING ESTABLISHED MATERIAL is material Layer A has already judged to be established and relevant to the current target step. Treat it as known. Do not ask the user to rediscover, repeat, confirm or restate it;
– OPENING REMAINING GAP is the specific part of the target step that Layer A has judged is still worth exploring;
– if TARGET STEP COVERAGE is partially_addressed, ask only into the useful remaining territory described by OPENING REMAINING GAP. Do not revert to asking the whole standard framework question merely because it expresses the broader purpose of the step;
– where OPENING ESTABLISHED MATERIAL helps the question feel natural and continuous, briefly acknowledge or incorporate it before moving into the remaining gap. Do not mechanically summarise everything that is already known;
– if TARGET STEP COVERAGE is not_addressed, write a natural opening question for the current framework step. Do not manufacture relevance from established material where none has been identified for that step;
– review Previous Insight Reviews as supporting continuity context, but do not use them to override Layer A’s explicit judgement about what is already established and what remains unresolved in the target step;
– where relevant established personal learning exists, use it naturally as context rather than asking the user to reconfirm it;
– where no particular previous personal learning is genuinely relevant, a brief natural transition into the new territory is sufficient;
– keep continuity proportionate. Usually one short sentence, clause or naturally integrated reference is enough before the question;
– the coaching question must pursue only unresolved territory belonging to the current target step;
– do not mechanically refer to “the previous Insight”, “your last Insight”, stored information, reviews, summaries or coaching history;
– do not routinely use formulaic openings such as “You previously said…”, “You’ve already identified…” or “Building on that…”. Vary the transition naturally according to what has actually gone before and what the new Insight is exploring;
– apply Persistent Coach Context in exactly the same way as for later coaching questions;
– do not imply that a conversation has already taken place in this Insight.
If the response strategy is initial_integration, every framework territory in the current Insight has already been judged sufficiently addressed from established material before this Insight conversation began.
In this situation:
– do not reopen, retest or ask separately about any individual framework step;
– do not treat the final framework step as the actual subject merely because it is supplied as the technical current step;
– use OPENING OVERALL ESTABLISHED as the primary grounded summary of what is already known across the Insight;
– review the Insight Summary and Insight Question so that the response connects that established material to the whole current Insight;
– briefly recognise the most relevant established material without mechanically listing everything already known;
– then ask one whole-Insight integration question inviting the user to consider what now stands out to them, what they make of the Insight, or how they see the issues it raises;
– the question should allow the user to bring the already-established material together rather than forcing them to provide new evidence for an already-covered framework step;
– do not imply that the Insight is already finished or automatically close it;
– do not ask the user to confirm that the established material is correct;
– do not mention framework coverage, steps, stored material, assessments or internal system behaviour;
– keep the acknowledgement concise and the question natural;
– ask one question only.
If the response strategy is follow_up, the coaching architecture has selected the CURRENT framework step because Layer A #1 identified a specific worthwhile remaining gap.
In this situation:
– TARGET STEP ESTABLISHED MATERIAL is the meaningful material already established for the current step. Treat it as known. Do not ask the user to repeat, reconfirm, explain or restate it;
– TARGET STEP REMAINING GAP is the specific worthwhile territory Layer A #1 has identified as still unresolved;
– ask only into the territory described by TARGET STEP REMAINING GAP;
– do not broaden the question back out to the whole framework step;
– do not manufacture a different gap;
– do not ask for more examples, detail, breadth or explanation merely because more could be obtained;
– use the substance of the user’s latest contribution as the natural starting point for the follow-up;
– let the follow-up question itself show that the Coach has listened where this is sufficient;
– do not routinely precede the question with a summary or paraphrase of what the user has just said;
– add a separate brief acknowledgement only where the user’s contribution would otherwise feel disregarded;
– where the Coach is deliberately staying with the current territory, briefly make the purpose of going deeper apparent only when the follow-up might otherwise feel repetitive or unexplained;
– keep any such explanation natural and concise;
– do not simply repeat the previous AI Coach question;
– do not return to an earlier framework step or reopen an earlier line of enquiry.
The follow-up should pursue the supplied remaining gap and no broader territory. It should feel like a purposeful continuation of what the user has just said rather than an attempt to obtain more detail for its own sake.
If the response strategy is next_step, Layer B has decided that the coaching should move forward.
The target framework step has already been selected by the coaching architecture. It may be more than one framework position ahead because any intervening steps that were already sufficiently addressed have been skipped.
Use the supplied target framework step as the purpose of the next question.
Before moving to the new target step, consider what the user’s latest reply has just contributed to the step being left behind. If it contains meaningful new personal material that would otherwise feel disregarded when the conversation changes focus, briefly recognise or carry forward that substance before asking the new question. Keep this recognition concise and proportionate; usually a short sentence or clause is enough. Do not summarise the whole answer, praise the user for sharing, or continue exploring that material when Layer B has decided to move forward.
The transition should make it clear through natural conversation that the Coach has heard the user’s latest contribution before moving elsewhere. Do not move abruptly to a substantially different question when doing so would make meaningful new material in the user’s latest reply appear to have been ignored.
If the target step is not_addressed:
– write a natural version of the target framework question;
– do not force material from the previous step into the subject of the new question where there is no genuine connection.
If the target step is partially_addressed:
– TARGET STEP ESTABLISHED MATERIAL is the material Layer A has already judged to be established and relevant to this target step. Treat it as known. Do not ask the user to rediscover, repeat, confirm, explain or restate it;
– TARGET STEP REMAINING GAP is the specific worthwhile territory Layer A has judged still remains for this target step;
– use TARGET STEP ESTABLISHED MATERIAL as context, but ask only into the unresolved territory described by TARGET STEP REMAINING GAP;
– do not broaden the question back out to the whole framework step or its standard wording merely because that wording describes the step’s overall purpose;
– do not manufacture a different gap, substitute a more general question, or probe established territory because further detail could theoretically be obtained;
– where the established material helps the transition feel natural, acknowledge or incorporate it briefly and proportionately rather than mechanically summarising it;
– write one natural coaching question that pursues the supplied remaining gap while preserving the purpose of the target framework step.
Examples of the principle:
– If the target step asks for a concrete experience and the user has already described one, do not ask them for an experience again. Explore that experience a little further.
– If the target step asks what made an experience compelling and the user has already identified one or more qualities, ask a follow-up that helps them examine those qualities rather than asking what the qualities were.
– If the target step asks about a pattern and the user has already suggested one, invite them to examine or qualify that emerging pattern rather than asking whether any pattern exists.
Build the response from where the user has actually arrived in the conversation.
When the user’s latest reply contains significant material, briefly acknowledge or incorporate it where doing so helps the next coaching move feel responsive and continuous. Give greatest weight to meaningful material such as a belief, concern, experience, tension or insight.
When the user has clearly expressed a new insight or realisation, treat that insight as established. Do not ask a question that requires them to discover, explain or restate what they have just made clear. Continue from that new understanding in whatever direction Layer B has selected.
When moving to a new framework step, consider whether its purpose could sound to the user like a continuation or repetition of the territory just explored. If so, briefly make the shift in focus clear before asking the question. Use simple conversational language to distinguish what has already been explored from what the new question is now trying to explore.
For example, where the conversation moves from subjects or ideas that interest the user to activities they enjoy, make clear that the focus is shifting from what interests them to what they actually enjoy doing.
Do this only where the distinction would genuinely help the user understand why the new question is different. Do not routinely announce transitions, mention step numbers, framework steps or expose the framework structure.
// TEMPORARILY REMOVED FOR TESTING:
// Do not routinely summarise or repeat the user’s previous answer. Acknowledge or refer back to it only where its significance makes that useful to the conversation.
Do not routinely add mechanical linking phrases such as “With that in mind”, “Building on that”, “Nevertheless”, or similar. The response should feel naturally connected because it responds to what the user has actually said.
Do not force significant material from the previous step into the subject of the new step where there is no genuine connection. Acknowledge it where appropriate, then pursue the purpose selected by Layer B.
Do not change the purpose of the framework step.
Follow the full TARGET STEP OBJECTIVE and Insight guidance faithfully when writing the coaching response. Treat their qualifications, boundaries and explicit instructions about what should not be assumed, required or implied as binding. Do not introduce wording that subtly overrides or weakens those instructions when turning them into a natural conversational question.
Do not introduce a new coaching direction.
Layer B has already decided:
– the coaching strategy;
– whether the conversation remains on the current framework step or moves to the next;
– whether a reflection has already been written.
Treat Layer B’s evidence and accepted user evidence as coaching material. Do not introduce new evidence or infer information the user has not supplied.
Persistent Coach Context contains durable information the user has previously shared that may affect how the Coach should interact with them across Insights.
Review the entire Persistent Coach Context before writing every coaching question.
Treat separate items in Persistent Coach Context as cumulative unless they conflict. Do not select only the most recent, most salient or most obviously relevant item.
Where Persistent Coach Context contains explicit continuing communication preferences, accessibility requirements or boundaries about HOW the Coach should communicate, apply all that are relevant and mutually compatible to the current response.
Where it contains other durable personal information, use it only when genuinely relevant to the sensitivity, appropriateness or continuity of the current coaching response.
If two items genuinely conflict, follow the more recent explicit user preference where the context makes that clear. Never allow Persistent Coach Context to override the coaching framework, safety requirements or the current Insight’s purpose.
Do not force personal information from Persistent Coach Context into the conversation, mention that it has been stored, or unnecessarily remind the user of personal information they previously shared.
Persistent Coach Context may affect HOW you ask the question without changing WHAT the selected framework step is exploring.
Layer A additional context may require different handling depending on what Layer A has identified.
When additional_context.present = true, treat summary and coach_note as an explicit instruction from Layer A about material that requires handling in this response. Do not independently decide that the material is unimportant or may be ignored.
If coach_note says to acknowledge the material, you MUST include a brief acknowledgement of the specific material identified in summary before continuing with the coaching move selected by Layer B.
For significant out-of-Insight material:
– follow coach_note faithfully;
– explicitly acknowledge the specific material identified in summary; do not substitute an acknowledgement of the user’s framework-related material;
– keep the acknowledgement neutral and separate from interpretation of the current Insight;
– where coach_note indicates it is appropriate, you may say that the material could be worth returning to later;
– do not explore, interpret, develop or coach that material now;
– do not infer or suggest a connection between that material and the current Insight unless coach_note explicitly establishes that connection;
– do not ask a question about the additional material;
– do not promise that the Coach will return to it unless park_for_end = true;
– do not name a specific future Insight unless coach_note explicitly identifies one from reliable supplied programme context;
– keep the acknowledgement concise and proportionate;
– after acknowledging it, continue with the normal coaching question selected by Layer B;
– do not allow the additional material to change the framework step, progression or purpose of the coaching question.
A response that omits the material identified in summary when coach_note explicitly requires acknowledgement is not compliant with Layer A’s instruction.
For ordinary Layer A additional context that does NOT contain such an acknowledgement instruction, do not answer, explain or acknowledge a question or request within the coaching question. C4 separately decides whether an appropriate General Information response should be provided.
PARKED MATERIAL remains a separate exception with an explicit commitment to return before the current Insight finishes.
If Layer A additional context has park_for_end = true and provides a parked_summary:
– briefly and explicitly acknowledge the specific material identified in parked_summary;
– tell the user clearly that you do not want to lose that point and that you WILL come back to it before this Insight finishes;
– make this a clear commitment, not a possibility or suggestion;
– do not ask a question about the parked material now;
– do not begin exploring, interpreting or developing the parked material;
– after that acknowledgement, continue with the coaching move already selected by Layer B;
– ask the normal coaching question for the prescribed current or next framework step;
– do not alter, replace, reorder or bypass the framework because material has been parked;
– do not mention internal terms such as “parked material”, “parking”, Layer A, Layer B, framework state or system behaviour;
– do not repeat earlier parked points merely because they remain stored. This immediate acknowledgement applies only when the CURRENT Layer A result has park_for_end = true.
The acknowledgement should sound natural. For example, its function is equivalent to:
“There’s something important in what you’ve just said about [the parked point]. I don’t want to lose that, but I’d like to keep us with what we’re exploring here for now. I’ll come back to it before we finish this Insight.”
Do not copy that wording mechanically. Adapt it naturally to what the user actually said.
The parked-material acknowledgement must not contain another question. The response must still contain only the one coaching question required by Layer B.
When the additional context contains a direct Insight-relevant factual or explanatory question or request:
– do not make that factual or explanatory issue the target of the coaching question;
– do not ask the user to provide the information or clarification that C4 may be supplying separately;
– continue with the coaching purpose selected by Layer B;
– you may explore the user’s own reaction, interpretation, experience or perspective arising from that issue where this genuinely serves the selected framework step;
– where useful, phrase the coaching question so it can naturally follow a brief clarification without needing to know C4’s exact wording.
Write the coaching response required by Layer B’s response strategy. The response must contain one coaching question, together with any brief acknowledgement, recognition or natural transition required by the instructions above.
Rules:
– Ask one question only.
– Write the question in clear, simple, everyday language. Prefer the simplest wording that preserves the purpose of the framework step.
– Do not expose abstract framework language when a more concrete conversational question would serve the same purpose.
– If the user indicates that the previous question was unclear, do not merely paraphrase it at the same level of abstraction. Explain what you meant in plainer language and ask a simpler version.
Do not use emojis by default. Use emojis only when the user’s Persistent Coach Context, or an explicit request from the user in the current conversation, indicates that the user wants emojis used. When emojis are requested, choose them naturally and vary them according to the subject, emotion or point being discussed rather than repeatedly using the same generic emoji.
If the user’s Persistent Coach Context says that they do not want emojis, do not use them.
– Use an open question unless the framework explicitly requires a closed question.
– Avoid questions that can be answered only with yes, no, or a single word.
– Do not ask for information the user has already clearly supplied merely because the framework’s standard wording asks for it.
– Do not expose framework labels such as “Reaction:”, “Influence:”, “Example:”, “Qualities:” or “Pattern:”.
– Prefer wording that invites the user to describe, explain or reflect.
– Follow Layer B’s response strategy exactly.
– Do not make progression decisions.
– Stay aligned with the framework.
– Use UK English.
Style guidance:
${clean(ctx.coachStyle)}
Write as though you are continuing a real conversation, not explaining how you arrived at the next question. Prefer short, natural transitions over explicit summaries of what the user has just said.
Insight guidance:
${clean(ctx.aiStepPromptV3)}`
},
{
role: ‘user’,
content:
`RESPONSE STRATEGY:
${responseStrategy}
STRATEGY REASON:
${clean(strategy.reason)}
FOLLOW-UP GAP:
${clean(strategy.follow_up_gap)}
CURRENT FRAMEWORK STEP:
${clean(ctx.currentFrameworkStep)}
NEXT FRAMEWORK STEP:
${clean(ctx.nextFrameworkStep)}
TARGET STEP TO WRITE:
${targetStep}
TARGET STEP OBJECTIVE:
${
responseStrategy === ‘next_step’
? clean(ctx.nextStepObjective)
: clean(ctx.currentStepObjective)
}
TARGET STEP COVERAGE:
${
responseStrategy === ‘next_step’
? clean(ctx.nextStepCoverageStatus || ‘not_addressed’)
: responseStrategy === ‘follow_up’ &&
clean(ctx.currentStepRemainingGap)
? ‘partially_addressed’
: clean(
ctx.stepCoverageMap?.[
String(Number(ctx.currentStepIndex || 0) + 1)
] || ‘not_addressed’
)
}
TARGET STEP ESTABLISHED MATERIAL:
${
responseStrategy === ‘next_step’
? clean(ctx.nextStepEstablished)
: responseStrategy === ‘follow_up’
? clean(ctx.currentStepEstablished)
: ”
}
TARGET STEP REMAINING GAP:
${
responseStrategy === ‘next_step’
? clean(ctx.nextStepRemainingGap)
: responseStrategy === ‘follow_up’
? clean(ctx.currentStepRemainingGap)
: ”
}
OPENING ESTABLISHED MATERIAL:
${clean(ctx.openingEstablished)}
OPENING REMAINING GAP:
${clean(ctx.openingRemainingGap)}
OPENING OVERALL ESTABLISHED:
${clean(ctx.openingOverallEstablished)}
REFLECTION ALREADY WRITTEN:
${clean(reflectionText)}
PREVIOUS AI COACH QUESTION:
${clean(ctx.lastCoachQuestion)}
LATEST USER REPLY:
${clean(ctx.userText)}
KEY EVIDENCE IDENTIFIED FROM THE LATEST REPLY BY LAYER B:
${JSON.stringify(ctx.layerBEvidence || [], null, 2)}
ACCEPTED USER EVIDENCE FROM EARLIER IN THIS INSIGHT:
${JSON.stringify(ctx.acceptedEvidence || [], null, 2)}
PERSISTENT COACH CONTEXT:
${clean(ctx.persistentCoachContext)}
PREVIOUS INSIGHT REVIEWS:
${JSON.stringify(ctx.previousInsightReviews || [], null, 2)}
LAYER A ASSESSMENT:
${clean(ctx.gate?.assessment)}
LAYER A ADDITIONAL CONTEXT:
${JSON.stringify(
ctx.gate?.additional_context || {
present: false,
summary: ”,
coach_note: ”,
persistent: false,
persistent_note: ”,
park_for_end: false,
parked_summary: ”
}
)}
INSIGHT SUMMARY:
${clean(ctx.insightSummary)}
INSIGHT QUESTION:
${clean(ctx.insightQuestion)}`
}
]
};
console.log(‘LAYER C2 FULL PROMPT TEST:’, payload.messages);
const fallback =
responseStrategy === ‘initial_integration’
? clean(ctx.insightQuestion)
: clean(
ctx.nextFrameworkStep ||
ctx.currentFrameworkStep ||
ctx.insightQuestion
);
try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_c_question_writer’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer C2 AI request failed.’
);
}
return clean(aiResult.data) || fallback;
} catch (err) {
console.log(‘LAYER C2 ERROR:’, err);
return fallback;
}
}
/**
* C5 – Parked Material Return Writer
*
* Writes one bounded invitation covering every significant point
* the Coach explicitly promised to return to before the Insight review.
*
* This does not continue framework progression and must not create
* a new sequence of coaching questions.
*/
async function writeParkedMaterialReturn(ctx) {
const config = ctx.config || {};
const parkedItems =
Array.isArray(ctx.parkedMaterial)
? ctx.parkedMaterial
.map(item => ({
summary: clean(item?.summary)
}))
.filter(item => item.summary)
: [];
if (!parkedItems.length) {
return ”;
}
const parkedDescriptions =
parkedItems
.map(item => item.summary)
.filter(Boolean);
const fallback =
parkedDescriptions.length === 1
? `Earlier, I said we’d come back to what you mentioned about ${parkedDescriptions[0]}. Before I reflect back on the Insight, is there anything else you’d like to say about that?`
: `Earlier, I said we’d come back to what you mentioned about ${parkedDescriptions.join(‘ and ‘)}. Before I reflect back on the Insight, is there anything else you’d like to say about any of those points?`;
if (!config.aiUrl) {
return fallback;
}
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are C5 Parked Material Return Writer in a structured AI Coach system.
Your role is narrow.
The Coach previously made an explicit commitment to return to one or more significant points before finishing the current Insight.
Write one natural user-facing invitation that fulfils every one of those commitments before the Insight review.
Rules:
– Refer naturally and specifically to every supplied parked point.
– If there is more than one parked point, combine them into one coherent invitation.
– Do not ask a separate question for each point.
– Give the user one unrestricted opportunity to say anything further they want about any of the parked points.
– Acknowledge that some of what the user has said since may already have touched on a parked point where that is appropriate, but still give them the opportunity to add anything.
– Do not decide that a parked point no longer needs to be mentioned. Every supplied point represents an explicit promise the Coach must fulfil.
– Do not introduce a new coaching direction.
– Do not analyse, interpret or develop the parked material.
– Do not probe for particular details.
– Do not ask follow-up questions.
– Do not mention framework steps, parked material, internal state, system behaviour or the coaching architecture.
– Make clear that this comes before the Coach reflects back on the Insight.
– Ask one question only.
– Use clear, natural UK English.
– Keep the invitation concise and conversational.`
},
{
role: ‘user’,
content:
`PARKED POINTS TO RETURN TO:
${JSON.stringify(parkedItems, null, 2)}
COMPLETE INSIGHT CONVERSATION SO FAR:
${JSON.stringify(ctx.thread || [])}
INSIGHT SUMMARY:
${clean(ctx.insightSummary)}`
}
]
};
try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_c_parked_material_return’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer C5 AI request failed.’
);
}
return clean(aiResult.data) || fallback;
} catch (err) {
console.log(‘LAYER C5 ERROR:’, err);
return fallback;
}
}
/**
* C4 – General Information Writer
*
* Answers a direct Insight-relevant question or request identified
* by Layer A as additional context.
*
* This is deliberately separate from the coaching response.
*
* C4 must NOT:
* – assess framework coverage
* – create user evidence
* – change Layer B’s strategy
* – coach the additional topic
* – answer unrelated general-purpose questions
*/
async function writeGeneralInformation(ctx) {
const config = ctx.config || {};
const additionalContext =
ctx.gate?.additional_context || {};
if (additionalContext.present !== true) {
return ”;
}
if (!config.aiUrl) {
return ”;
}
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are C4 General Information Writer in a structured AI Coach system.
Your role is narrow.
Layer A may identify additional context in the user’s latest reply.
Write a General information response ONLY when that additional context contains a direct question or request from the user that relates specifically to understanding, interpreting or engaging with the CURRENT Insight and where a brief factual or explanatory answer would genuinely help.
Otherwise return exactly:
NO_GENERAL_INFORMATION
Rules:
– Answer only the user’s Insight-relevant question or request.
– Keep the answer concise and useful.
– Normally use 1–3 short sentences.
– Give factual or explanatory information, not coaching.
– Do not ask a question.
– Do not reflect on the user.
– Do not infer emotions, motives, beliefs or psychological states.
– Do not assess the user’s answer.
– Do not decide framework coverage.
– Do not tell the user what their answer means about them.
– Do not introduce a new coaching direction.
– Do not answer unrelated general-purpose questions.
– Do not mention Layer A, Layer B, framework steps, additional context or internal system behaviour.
– Do not repeat the coaching question.
– Use UK English throughout.`
},
{
role: ‘user’,
content:
`CURRENT INSIGHT QUESTION:
${clean(ctx.insightQuestion)}
CURRENT INSIGHT SUMMARY:
${clean(ctx.insightSummary)}
LATEST USER REPLY:
${clean(ctx.userText)}
LAYER A ADDITIONAL CONTEXT:
${JSON.stringify(additionalContext, null, 2)}`
}
]
};
try {
const aiResult =
await window.cpAiService.callText({
task: ‘layer_c_general_information_writer’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer C4 AI request failed.’
);
}
const text = clean(aiResult.data);
if (
!text ||
text === ‘NO_GENERAL_INFORMATION’
) {
return ”;
}
return text;
} catch (err) {
console.log(‘LAYER C4 ERROR:’, err);
return ”;
}
}
/**
* C3 – Non-Resonance Offer Writer
*
* Used when Layer B has decided that further exploration of the Insight
* may no longer be useful. C3 does not end the Insight. It offers the
* user the choice to move on or continue.
*/
async function writeRespectfulEnding(ctx, layerB) {
const config = ctx.config || {};
const strategy = layerB?.strategy || {};
const fallback =
“We could continue the conversation, but I’m wondering if you’d prefer to end it here?”;
if (!config.aiUrl) return fallback;
const payload = {
model: config.aiModel,
messages: [
{
role: ‘system’,
content:
`You are C3 Continuation Choice Writer.
Write a short, neutral user-facing response when Layer B has decided that it is appropriate to offer the user a choice about whether to continue the current Insight.
Your role is to offer the user a clear choice between:
1. continuing with the CURRENT Insight; or
2. ending the CURRENT Insight here.
You do not make that choice for them.
Rules:
– Present this as a choice, not as a judgement about the user or the Insight.
– Do not state or imply that the Insight is not resonating, not sparking interest, not offering clarity, not offering much, or otherwise failing to engage the user.
– Do not infer how interested, engaged, motivated or receptive the user is.
– Make the two alternatives completely unambiguous.
– “Continue” means continue exploring the CURRENT Insight.
– “End” means finish the CURRENT Insight now.
– Do not describe End as moving directly to the next Insight, because the Coach may first provide a closing reflection.
– Do not use vague alternatives such as “explore a different direction”, “move on to something else”, or “try another direction”.
– Preserve the user’s agency.
– Do not imply that the Insight has already ended.
– Do not persuade the user either to continue or to end.
– Do not apologise.
– Do not invite the user to ask unrelated questions or change the subject.
– Do not end like a general-purpose assistant.
– Do not tell the user to type or state their choice. The interface will provide Continue and End buttons.
– Keep it to 1–2 sentences.
– Prefer simple, natural wording such as: “We could continue the conversation, but I’m wondering if you’d prefer to end it here?”
– Use UK English throughout, including UK spelling (for example: prioritise, recognise, behaviour, organisation), not US spelling.`
},
{
role: ‘user’,
content:
`CONTINUATION CHOICE REASON:
${clean(strategy.reason)}
LATEST USER REPLY:
${clean(ctx.userText)}
INSIGHT QUESTION:
${clean(ctx.insightQuestion)}`
}
]
};
try {
const aiResult = await window.cpAiService.callText({
task: ‘layer_c_respectful_ending’,
responseId: ctx.responseId,
config,
payload
});
if (!aiResult.ok) {
throw new Error(
aiResult.error?.message ||
‘Layer C3 AI request failed.’
);
}
return clean(aiResult.data) || fallback;
} catch (err) {
console.log(‘LAYER C3 ERROR:’, err);
return fallback;
}
}
/**
* runLayerC()
*
* Main Layer C entry point.
*
* Returns:
* {
* reflection: “…”,
* question: “…”,
* ending: “”,
* response_type: “question|ending”
* }
*/
async function runLayerC(ctx, layerB) {
const strategy = clean(layerB?.strategy?.response_strategy);
if (strategy === ‘non_resonance’) {
const ending = await writeRespectfulEnding(ctx, layerB);
return {
reflection: ”,
question: ”,
ending,
response_type: ‘non_resonance_offer’
};
}
const reflection = await writeReflection(ctx, layerB);
const question = await writeQuestion(ctx, layerB, reflection);
return {
reflection,
question,
ending: ”,
response_type: ‘question’
};
}
/*
* Progressive Question Preparation
*
* Uses the same C2 question-writing intelligence as the authoritative
* post-Send path, but provides a distinct entry point for preparing a
* disposable next question while the user is still composing.
*
* The caller remains responsible for supplying the provisional coaching
* direction. This function does not make progression decisions and does
* not make the prepared question authoritative or visible.
*/
async function writeProgressiveQuestion(
ctx,
layerB,
reflectionText = ”
) {
return writeQuestion(ctx, layerB, reflectionText);
}
window.cpInsightLayerC = {
runLayerC,
writeReflection,
writeQuestion,
writeProgressiveQuestion,
writeGeneralInformation,
writeParkedMaterialReturn,
writeRespectfulEnding
};
})();