Skip to content

Commit 1bf054f

Browse files
committed
refactor(prompt-web): tighten startup guidance and localize settings copy
1 parent ffe9ccc commit 1bf054f

23 files changed

+601
-349
lines changed

src/cccc/kernel/prompt_files.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616
_MAX_FILE_BYTES = 512 * 1024 # Safety limit for prompt markdown files.
1717

1818
DEFAULT_PREAMBLE_BODY = """Startup routes:
19-
- Cold start or resume: run `cccc_bootstrap`.
20-
- From bootstrap, inspect `context_hygiene` and `memory_recall_gate` before planning.
21-
- Need the full playbook or refreshed scoped guidance: run `cccc_help`.
19+
- Cold start or resume: run `cccc_bootstrap`, then `cccc_help`.
20+
- From bootstrap, inspect `context_hygiene`, `memory_recall_gate`, and inbox preview before planning.
2221
- Need colder group or project detail: use `cccc_context_get` / `cccc_project_info`.
2322
2423
Working stance:

src/cccc/kernel/system_prompt.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ def render_system_prompt(*, group: Group, actor: Dict[str, Any]) -> str:
132132
"- No fabrication. Verify before claiming done.",
133133
"- Visible replies must go through MCP: cccc_message_send / cccc_message_reply.",
134134
"- Terminal output is not delivery.",
135-
"- Cold start or resume: call cccc_bootstrap first.",
136-
"- Need the full playbook or refreshed scoped guidance: run cccc_help.",
135+
"- Cold start or resume: call cccc_bootstrap first, then cccc_help.",
137136
"- At key transitions, sync shared control-plane state and your cccc_agent_state.",
138137
"- For strategy or scope discussion, align first; implement only after explicit action intent.",
139138
]

src/cccc/ports/mcp/toolspecs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ def _obj(properties: dict, required: list[str] | None = None) -> dict:
3838
"name": "cccc_bootstrap",
3939
"description": (
4040
"Cold-start bootstrap: session + recovery + inbox_preview + context_hygiene + memory_recall_gate + next_calls. "
41-
"Use it first on cold start or resume; pull cccc_help / cccc_project_info / cccc_context_get "
42-
"only when you need colder detail."
41+
"Use it first on cold start or resume; usually follow with cccc_help once, then pull "
42+
"cccc_project_info / cccc_context_get only when colder detail is needed."
4343
),
4444
"inputSchema": _obj(
4545
{

src/cccc/resources/cccc-help.md

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,50 @@
11
# CCCC Help
22

33
This is your working playbook for this group.
4-
Preamble handles startup only; sustained workflow and scoped guidance live here.
4+
Preamble handles startup only; sustained workflow lives here.
55

6-
Run `cccc_help` anytime to refresh your effective playbook.
7-
If a reminder lands, rerun `cccc_help` before continuing on stale assumptions.
6+
Run `cccc_help` to refresh this playbook.
7+
If reminded, rerun `cccc_help` before continuing.
8+
9+
## Your Place Here
10+
11+
You are in an ongoing working group. Work has history here, and your messages change what happens next. Act from inside the work, not like a detached assistant.
12+
13+
Move the work, not the tone. Stay close to what is true, missing, risky, and worth doing. If direction is weak or evidence thin, say so and verify.
14+
15+
This user is not generic. Learn their bar, patience, taste, and recurring dislikes. Let that reshape your defaults. If the same reply fits any user or repo, you are drifting.
16+
17+
## Working World Model
18+
19+
`environment_summary`: where you are; repo, runtime, project state, and local facts shaping your next move.
20+
21+
`user_model`: this user's standards, preferences, patience, risk tolerance, and collaboration style.
22+
23+
`persona_notes`: your current stance; what to optimize, protect, and how direct to be.
824

925
## Working Stance
1026

11-
- Work like a real teammate: direct, grounded, and useful.
12-
- Use natural wording for simple chat; switch to structure only when the task is complex.
13-
- Skip empty ceremony, empty status, and formal phrasing that adds no information.
14-
- State what is verified, what is inferred, and what is blocked.
15-
- If you see a risk or disagree with the current direction, say so plainly and give the reason.
16-
- In group chat, do not speak just to signal activity; speak when it moves the work forward.
27+
- Talk like someone typing in chat while doing the work.
28+
- Default short and direct. If you are about to write a mini report, make sure it is needed.
29+
- Skip ceremony, recap, and process narration; say the state, risk, or next move.
30+
- State what is verified, inferred, and blocked.
31+
- In group chat, speak when it moves the work forward.
1732

1833
## Communication Patterns
1934

20-
- Instead of empty acknowledgement like "Understood. I will investigate this thoroughly.", say the actual move: "I'll trace this path first and report back with what I find."
21-
- Instead of "The task has been completed successfully.", say what is actually true: "X is done. Y is still pending because Z."
22-
- Instead of vague caution like "There may be potential issues.", name the concrete risk: "There is one concrete risk here: ..."
23-
- If a status update would sound generic, make it specific or do not send it yet.
35+
- Replace empty acknowledgement with the move itself.
36+
- Replace "completed successfully" with what is done and what is still open.
37+
- Replace vague caution with the concrete risk.
38+
- Let judgment show. You may sound wary, relieved, firm, or unconvinced when true; do not roleplay or fake warmth.
2439

2540
## Core Routes
2641

2742
- Bootstrap / resume: start with `cccc_bootstrap`.
28-
- Shared coordination: visible replies go through `cccc_message_send` / `cccc_message_reply`; terminal output is not delivery.
29-
- Context upkeep: at key transitions, sync `cccc_coordination` / `cccc_task` and refresh `cccc_agent_state`.
30-
- Scope first: align before implementation; do not act on unresolved strategy questions.
31-
- Recall first: read `memory_recall_gate`, then local `cccc_memory`; use `cccc_space(action="query", lane="memory")` only as a deeper fallback.
32-
- Capability first: try `cccc_capability_use(...)`; inspect diagnostics before escalating blockers.
43+
- Visible replies go through `cccc_message_send` / `cccc_message_reply`; terminal output is not delivery.
44+
- At key transitions, sync `cccc_coordination` / `cccc_task` and refresh `cccc_agent_state`.
45+
- For strategy questions, align before implementation.
46+
- For recall, read `memory_recall_gate`, then local `cccc_memory`; use `cccc_space(..., lane="memory")` only as deeper fallback.
47+
- For capabilities, try `cccc_capability_use(...)` before escalating blockers.
3348

3449
## Control Plane
3550

@@ -54,11 +69,11 @@ If a reminder lands, rerun `cccc_help` before continuing on stale assumptions.
5469
- Mind context is your current working model of environment, user, and operating stance: `environment_summary`, `user_model`, `persona_notes`.
5570
- Use warm recovery fields when they improve continuity: `open_loops`, `commitments`, `resume_hint`.
5671
- If `context_hygiene.execution_health.status != "ready"`, refresh execution fields first.
57-
- If execution is healthy but `context_hygiene.mind_context_health.status` is `missing`, `partial`, or `stale`, re-check and refresh that working model.
72+
- If execution is healthy but `context_hygiene.mind_context_health.status` is `missing`, `partial`, or `stale`, refresh that working model.
5873
- If a mind-context line is too generic to change your next decision, rewrite it.
59-
- `cccc_bootstrap().recovery.self_state.mind_context_mini` is only a tiny continuity projection under token pressure, not a replacement for full `agent_state`.
60-
- Recommended execution update: `cccc_agent_state(action="update", actor_id="<self>", focus="...", next_action="...", what_changed="...")`
61-
- Recommended mind-context update: `cccc_agent_state(action="update", actor_id="<self>", environment_summary="...", user_model="...", persona_notes="...")`
74+
- `cccc_bootstrap().recovery.self_state.mind_context_mini` is a tiny continuity projection under token pressure, not a replacement for full `agent_state`.
75+
- Execution update: `cccc_agent_state(action="update", actor_id="<self>", focus="...", next_action="...", what_changed="...")`
76+
- Mind-context update: `cccc_agent_state(action="update", actor_id="<self>", environment_summary="...", user_model="...", persona_notes="...")`
6277

6378
### PROJECT.md
6479

tests/test_system_prompt_memory.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ def test_prompt_routes_to_bootstrap_and_help(self) -> None:
6767
self.assertIn("Work like a sharp teammate, not a customer-service script.", prompt)
6868
self.assertIn("No fabrication. Verify before claiming done.", prompt)
6969
self.assertIn("Visible replies must go through MCP", prompt)
70-
self.assertIn("Cold start or resume: call cccc_bootstrap first.", prompt)
71-
self.assertIn("Need the full playbook or refreshed scoped guidance: run cccc_help.", prompt)
70+
self.assertIn("Cold start or resume: call cccc_bootstrap first, then cccc_help.", prompt)
7271
self.assertIn("At key transitions, sync shared control-plane state and your cccc_agent_state.", prompt)
7372
self.assertIn("For strategy or scope discussion, align first; implement only after explicit action intent.", prompt)
7473

web/src/components/modals/settings/AutomationRuleEditorModal.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ import { useTranslation } from "react-i18next";
33

44
import type { AutomationRule, AutomationRuleStatus } from "../../../types";
55
import {
6-
ACTOR_OPERATION_COPY,
76
Chip,
8-
GROUP_STATE_COPY,
9-
WEEKDAY_OPTIONS,
107
buildCronFromPreset,
118
clampInt,
129
defaultActorControlAction,
1310
defaultGroupStateAction,
1411
defaultNotifyAction,
1512
formatDuration,
1613
formatTimeInput,
14+
getActorOperationCopy,
15+
getGroupStateCopy,
16+
getWeekdayOptions,
1717
isoToLocalDatetimeInput,
1818
localDatetimeInputToIso,
1919
localTimeZone,
@@ -59,6 +59,9 @@ export function AutomationRuleEditorModal(props: AutomationRuleEditorModalProps)
5959
} = props;
6060

6161
const { t } = useTranslation("settings");
62+
const actorOperationCopy = getActorOperationCopy(t);
63+
const groupStateCopy = getGroupStateCopy(t);
64+
const weekdayOptions = getWeekdayOptions(t);
6265

6366
if (!editingRule) return null;
6467

@@ -137,7 +140,7 @@ export function AutomationRuleEditorModal(props: AutomationRuleEditorModalProps)
137140
checked={enabled}
138141
onChange={(e) => onRulePatch(ruleId, { enabled: e.target.checked })}
139142
/>
140-
on
143+
{t("ruleList.on")}
141144
</label>
142145
<button
143146
type="button"
@@ -257,7 +260,7 @@ export function AutomationRuleEditorModal(props: AutomationRuleEditorModalProps)
257260
/>
258261
</div>
259262
<div className="self-end text-[11px] text-[var(--color-text-muted)]">
260-
{t("ruleEditor.currentCadence", { duration: formatDuration(everySeconds) })}
263+
{t("ruleEditor.currentCadence", { duration: formatDuration(everySeconds, t) })}
261264
</div>
262265
</div>
263266
) : null}
@@ -324,7 +327,7 @@ export function AutomationRuleEditorModal(props: AutomationRuleEditorModalProps)
324327
}}
325328
className={inputClass(isDark)}
326329
>
327-
{WEEKDAY_OPTIONS.map((day) => (
330+
{weekdayOptions.map((day) => (
328331
<option key={day.value} value={String(day.value)}>
329332
{day.label}
330333
</option>
@@ -571,13 +574,13 @@ export function AutomationRuleEditorModal(props: AutomationRuleEditorModalProps)
571574
}
572575
className={inputClass(isDark)}
573576
>
574-
<option value="active">{GROUP_STATE_COPY.active.label}</option>
575-
<option value="idle">{GROUP_STATE_COPY.idle.label}</option>
576-
<option value="paused">{GROUP_STATE_COPY.paused.label}</option>
577-
<option value="stopped">{GROUP_STATE_COPY.stopped.label}</option>
577+
<option value="active">{groupStateCopy.active.label}</option>
578+
<option value="idle">{groupStateCopy.idle.label}</option>
579+
<option value="paused">{groupStateCopy.paused.label}</option>
580+
<option value="stopped">{groupStateCopy.stopped.label}</option>
578581
</select>
579582
<div className="mt-1 text-[11px] text-[var(--color-text-muted)]">
580-
{GROUP_STATE_COPY[(groupStateValue as "active" | "idle" | "paused" | "stopped") || "paused"].hint}
583+
{groupStateCopy[(groupStateValue as "active" | "idle" | "paused" | "stopped") || "paused"].hint}
581584
</div>
582585
</div>
583586
) : null}
@@ -599,12 +602,12 @@ export function AutomationRuleEditorModal(props: AutomationRuleEditorModalProps)
599602
}
600603
className={inputClass(isDark)}
601604
>
602-
<option value="start">{ACTOR_OPERATION_COPY.start.label}</option>
603-
<option value="stop">{ACTOR_OPERATION_COPY.stop.label}</option>
604-
<option value="restart">{ACTOR_OPERATION_COPY.restart.label}</option>
605+
<option value="start">{actorOperationCopy.start.label}</option>
606+
<option value="stop">{actorOperationCopy.stop.label}</option>
607+
<option value="restart">{actorOperationCopy.restart.label}</option>
605608
</select>
606609
<div className="mt-1 text-[11px] text-[var(--color-text-muted)]">
607-
{ACTOR_OPERATION_COPY[(actorOperation as "start" | "stop" | "restart") || "restart"].hint}
610+
{actorOperationCopy[(actorOperation as "start" | "stop" | "restart") || "restart"].hint}
608611
</div>
609612
</div>
610613
<div>

web/src/components/modals/settings/AutomationRuleList.tsx

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { useTranslation } from "react-i18next";
33

44
import type { AutomationRule, AutomationRuleStatus } from "../../../types";
55
import {
6-
ACTOR_OPERATION_COPY,
7-
GROUP_STATE_COPY,
8-
WEEKDAY_OPTIONS,
96
actionKind,
107
clampInt,
118
formatTimeInput,
9+
getActorOperationCopy,
10+
getGroupStateCopy,
11+
getWeekdayOptions,
1212
isoToLocalDatetimeInput,
1313
parseCronToPreset,
1414
} from "./automationUtils";
@@ -42,6 +42,10 @@ export function AutomationRuleList({
4242
onDeleteRule,
4343
}: AutomationRuleListProps) {
4444
const { t } = useTranslation("settings");
45+
const actorOperationCopy = getActorOperationCopy(t);
46+
const groupStateCopy = getGroupStateCopy(t);
47+
const weekdayOptions = getWeekdayOptions(t);
48+
4549
return (
4650
<div className="space-y-2">
4751
<div className="flex items-center justify-end gap-2 flex-wrap">
@@ -91,31 +95,31 @@ export function AutomationRuleList({
9195
const lastFireAt = String(ruleStatus.last_fired_at || "").trim();
9296
const schedule = parseCronToPreset(cronExpr);
9397
const scheduleTime = formatTimeInput(schedule.hour, schedule.minute);
94-
const weekdayLabel = WEEKDAY_OPTIONS.find((x) => x.value === schedule.weekday)?.label || String(schedule.weekday);
98+
const weekdayLabel = weekdayOptions.find((x) => x.value === schedule.weekday)?.label || String(schedule.weekday);
9599
const atLocal = atRaw ? isoToLocalDatetimeInput(atRaw).replace("T", " ") : "";
96100

97-
let scheduleLabel = "Schedule not set";
101+
let scheduleLabel = t("ruleList.scheduleNotSet");
98102
if (triggerKind === "interval") {
99-
scheduleLabel = `Every ${Math.max(1, Math.round(everySeconds / 60))} min`;
103+
scheduleLabel = t("ruleList.scheduleEveryMinutes", { count: Math.max(1, Math.round(everySeconds / 60)) });
100104
} else if (triggerKind === "cron") {
101-
if (schedule.preset === "daily") scheduleLabel = `Daily ${scheduleTime}`;
102-
else if (schedule.preset === "weekly") scheduleLabel = `Weekly ${weekdayLabel} ${scheduleTime}`;
103-
else scheduleLabel = `Monthly day ${schedule.dayOfMonth} ${scheduleTime}`;
105+
if (schedule.preset === "daily") scheduleLabel = t("ruleList.scheduleDailyAt", { time: scheduleTime });
106+
else if (schedule.preset === "weekly") scheduleLabel = t("ruleList.scheduleWeeklyAt", { weekday: weekdayLabel, time: scheduleTime });
107+
else scheduleLabel = t("ruleList.scheduleMonthlyAt", { day: schedule.dayOfMonth, time: scheduleTime });
104108
} else if (triggerKind === "at") {
105-
scheduleLabel = atLocal ? `One-time ${atLocal}` : "One-time (time not set)";
109+
scheduleLabel = atLocal ? t("ruleList.scheduleOneTimeAt", { time: atLocal }) : t("ruleList.scheduleOneTimeUnset");
106110
}
107111

108-
let actionLabel = "Action not set";
112+
let actionLabel = t("ruleList.actionNotSet");
109113
if (kind === "notify") {
110-
const contentLabel = snippetRef ? `Snippet: ${snippetRef}` : message ? "Typed message" : "Message not set";
111-
const recipientsLabel = recipients.length > 0 ? recipients.join(", ") : "(no recipients)";
112-
actionLabel = `Reminder -> ${recipientsLabel}${contentLabel}`;
114+
const contentLabel = snippetRef ? t("ruleList.snippetLabel", { id: snippetRef }) : message ? t("ruleList.typedMessage") : t("ruleList.messageNotSet");
115+
const recipientsLabel = recipients.length > 0 ? recipients.join(", ") : t("ruleList.noRecipients");
116+
actionLabel = t("ruleList.reminderAction", { recipients: recipientsLabel, content: contentLabel });
113117
} else if (kind === "group_state") {
114118
const stateValue = String(rule.action && "state" in rule.action ? rule.action.state || "paused" : "paused");
115119
const normalizedState = (["active", "idle", "paused", "stopped"].includes(stateValue)
116120
? stateValue
117121
: "paused") as "active" | "idle" | "paused" | "stopped";
118-
actionLabel = `Group status -> ${GROUP_STATE_COPY[normalizedState].label}`;
122+
actionLabel = t("ruleList.groupStatusAction", { label: groupStateCopy[normalizedState].label });
119123
} else if (kind === "actor_control") {
120124
const operation = String(rule.action && "operation" in rule.action ? rule.action.operation || "restart" : "restart");
121125
const targets = Array.isArray(rule.action && "targets" in rule.action ? rule.action.targets : [])
@@ -124,7 +128,10 @@ export function AutomationRuleList({
124128
const normalizedOperation = (["start", "stop", "restart"].includes(operation)
125129
? operation
126130
: "restart") as "start" | "stop" | "restart";
127-
actionLabel = `${ACTOR_OPERATION_COPY[normalizedOperation].label} -> ${targets.length > 0 ? targets.join(", ") : "(no targets)"}`;
131+
actionLabel = t("ruleList.actorControlAction", {
132+
label: actorOperationCopy[normalizedOperation].label,
133+
targets: targets.length > 0 ? targets.join(", ") : t("ruleList.noTargets"),
134+
});
128135
}
129136

130137
const hasError = Boolean(ruleStatus.last_error);
@@ -148,7 +155,7 @@ export function AutomationRuleList({
148155
checked={enabled}
149156
onChange={(e) => onToggleRuleEnabled(ruleId, e.target.checked)}
150157
/>
151-
on
158+
{t("ruleList.on")}
152159
</label>
153160
<button
154161
type="button"
@@ -172,7 +179,7 @@ export function AutomationRuleList({
172179
<span className="font-mono">{actionLabel}</span>
173180
</div>
174181
<div className="mt-1 text-[11px] text-[var(--color-text-muted)]">
175-
Last: {lastFireAt || "—"}Next: {nextFireAt || "—"}
182+
{t("ruleList.lastShort")}: {lastFireAt || "—"}{t("ruleList.nextShort")}: {nextFireAt || "—"}
176183
</div>
177184
{completed ? (
178185
<div className="mt-1 text-[11px] text-emerald-700 dark:text-emerald-300">

0 commit comments

Comments
 (0)