diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..716badf
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,47 @@
+# Работа над этим расширением
+
+## Никогда не писать в dconf живой сессии
+
+**Запрещено** выполнять `gsettings set`, `gsettings reset`, `dconf write` и `dconf reset`
+из bash этой сессии — ни голыми, ни с префиксом `XDG_CONFIG_HOME=...`.
+
+Почему префикс не помогает: базу выбирает не клиент, а `dconf-service`, поднятый шиной
+живой сессии, и берёт он её из **своего** окружения. Любая запись уходит в
+`~/.config/dconf/user` Антона.
+
+Чем это кончалось (дважды): затёрт `org.gnome.shell enabled-extensions` — во второй раз,
+9 и 10 августа 2026, на работающем shell, отчего у него мгновенно отвалились dock, ding,
+appindicators и tiling-assistant. Прежний список dconf не хранит, восстановить его точно
+нельзя, только реконструировать.
+
+Что делать вместо:
+
+- нужно поменять настройку в **живой** сессии — отдать Антону команду, он выполнит её сам
+ через `! <команда>`;
+- нужна настройка для **вложенного** shell — экспортировать `XDG_CONFIG_HOME` до форка
+ шины и звать `gsettings` уже внутри этой шины (см. рецепт ниже);
+- читать (`dconf read`, `gsettings get`) можно свободно.
+
+## Проверка изменений — только во вложенном shell
+
+Живую сессию не трогаем вообще: расширение включает и перезагружает shell Антон сам.
+У него несколько сессий Claude Code в zellij внутри gnome-terminal, и падение
+gnome-shell унесёт весь его рабочий набор.
+
+Рецепт для проверок только на чтение (GNOME 46, проверен 10.08.2026):
+
+```sh
+XDG_STATE_HOME=$TMP/state timeout 50 dbus-run-session -- \
+ gnome-shell --nested --wayland > $TMP/nested.log 2>&1
+```
+
+Подставные файлы сессий обязаны нести `event_ts` (плюс `since`, `started`): без него
+`SessionStore` считает их древними и **удаляет** до первой отрисовки.
+
+Скриншот из вложенного shell снять нельзя. Чтобы увидеть, что он нарисовал, добавляем
+временный `log()` в код, читаем его из `nested.log` — и убираем инструментацию перед
+коммитом.
+
+## Коммиты
+
+Без трейлера `Co-Authored-By`.
diff --git a/README.md b/README.md
index 7a8f97b..5a7b09b 100644
--- a/README.md
+++ b/README.md
@@ -47,9 +47,12 @@ Code ждёт меня прямо сейчас?**
достаточно открытых сессий сдвинули бы часы с центра.
Чипы идут по срочности, а внутри одного состояния первой стоит **самая
-давняя**: забывается та, что ждёт дольше всех, а не последняя. Время в
-состоянии показывается только у первого чипа: пять счётчиков рядом — это
-ряд чисел, а не ответ на вопрос.
+давняя**: забывается та, что ждёт дольше всех, а не последняя. Время
+показывается у **работающих** чипов — у всех сразу: именно у них счётчик о
+чём-то говорит (пять минут — это сборка, сорок — сессия во что-то упёрлась).
+У ждущих его нет: их возраст говорит лишь о том, когда вы перестали смотреть,
+а это и так видно по самому чипу. Счётчик отключается в настройках
+(«Show working time»); в меню возраст показывается у всех строк в любом случае.
## Место в панели
diff --git a/lib/indicator.js b/lib/indicator.js
index d4a42c8..38a91fe 100644
--- a/lib/indicator.js
+++ b/lib/indicator.js
@@ -61,6 +61,7 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
this._store = new SessionStore();
this._zellij = new ZellijTabs();
this._chipLabels = new Map();
+ this._ageLabels = new Map();
this._rows = [];
this._buildPanel();
@@ -222,21 +223,24 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
const shown = sessions.slice(0, maxChips);
const hidden = sessions.length - shown.length;
- // Age rides on the first chip only. Sessions are sorted by urgency, so
- // that is the one whose age decides anything; five ages side by side
- // would just be a wide row of numbers.
- const ageOnFirst = showAge && shown.length > 0;
+ // Age rides on the working chips, and on all of them. A session that
+ // is working is the one you might be waiting on, and how long it has
+ // been at it is the whole question -- five minutes is a build, forty
+ // is a session stuck on something. The states where nothing is
+ // happening have no such clock: their age says how long ago you
+ // stopped looking, which the row's own presence already says.
+ const ageOn = session => showAge && session.state === 'busy';
const signature = shown
.map(s => `${s.sessionId}:${s.state}:${labelFor(s)}`)
- .join('|') + `|${ageOnFirst}|${hidden}`;
+ .join('|') + `|${showAge}|${hidden}`;
if (signature !== this._chipSignature) {
this._chipBox.destroy_all_children();
- this._ageLabel = null;
- shown.forEach((session, i) => {
+ this._ageLabels = new Map();
+ shown.forEach(session => {
const { chip, age } = this._buildChip(
- session, labelFor(session), ageOnFirst && i === 0);
+ session, labelFor(session), ageOn(session));
if (age)
- this._ageLabel = { age, sessionId: session.sessionId };
+ this._ageLabels.set(session.sessionId, age);
this._chipBox.add_child(chip);
});
if (hidden > 0) {
@@ -249,13 +253,16 @@ class ClaudeStatusIndicator extends PanelMenu.Button {
this._chipSignature = signature;
}
- if (this._ageLabel) {
- // Looked up again rather than captured: a session that returns to
- // the same state within one refresh keeps the signature unchanged,
- // and a captured object would then show an age that stopped moving.
- const current = sessions.find(s => s.sessionId === this._ageLabel.sessionId);
- if (current)
- this._ageLabel.age.text = formatAge(this._ageOf(current));
+ // Looked up again rather than captured: a session that returns to the
+ // same state within one refresh keeps the signature unchanged, and a
+ // captured object would then show an age that stopped moving.
+ if (this._ageLabels.size) {
+ const byId = new Map(sessions.map(s => [s.sessionId, s]));
+ for (const [sessionId, label] of this._ageLabels) {
+ const current = byId.get(sessionId);
+ if (current)
+ label.text = formatAge(this._ageOf(current));
+ }
}
}
diff --git a/prefs.js b/prefs.js
index 9d934c6..b7d7a46 100644
--- a/prefs.js
+++ b/prefs.js
@@ -52,8 +52,8 @@ export default class ClaudeCodeStatusPreferences extends ExtensionPreferences {
_('The most urgent sessions get a chip; the rest are counted as “+N”.'),
1, 12));
dispGroup.add(this._switchRow(settings, 'show-age',
- _('Show time in state'),
- _('Shown on the most urgent session only.')));
+ _('Show working time'),
+ _('A counter on every session that is working, so you can see how long it has been at it. The menu lists ages either way.')));
const zellijGroup = new Adw.PreferencesGroup({
title: _('zellij'),
diff --git a/schemas/org.gnome.shell.extensions.claude-code-status.gschema.xml b/schemas/org.gnome.shell.extensions.claude-code-status.gschema.xml
index 0afaf58..95ceade 100644
--- a/schemas/org.gnome.shell.extensions.claude-code-status.gschema.xml
+++ b/schemas/org.gnome.shell.extensions.claude-code-status.gschema.xml
@@ -42,8 +42,8 @@
true
- Show how long the session has been in this state
- How long a session has been working, or waiting, is what decides whether to switch to it now or leave it running.
+ Show how long each working session has been at it
+ A counter on every chip that is working, which is what says whether to wait for it or go and look: five minutes is a build, forty is a session stuck on something. The idle states get no counter — their age only says how long ago you stopped looking.
true