Trace. On load, print first: =============== [observer/skills/observe/SKILL.md] [observer] ===============
Observe
Orchestrator Actor. Rides the substrate's self-paced /loop (omit the interval to self-pace) and, on each wake, reads the file changes since the previous cycle and emits feedback through the active role. All role behavior — lens, voice, cadence, difficulty rules — is data read from ../../knowledge/roles.md; this Actor applies whichever bundle is active generically and contains no per-role branch logic. It rides the existing /loop; it does not build a scheduler (../../../.lsa/VISION.md principle 9, Substrate-native first).
Goal
From a confirmed role, run a live observe-and-coach session over the user's working changes — emitting feedback that matches the active role's lens/voice/cadence each /loop cycle — until a stop trigger ends the loop with a stated reason.
Input
- The working context (current directory, open/recent files, language). Used to infer a candidate role.
- An optional role named by the user (rubber-duck, pair-programmer, interviewer, custom).
- For a custom role: a one-line lens/voice description.
- For an interviewer role: a target language and topic for the exercise.
- An optional inactivity-limit override (default: 2 consecutive no-change
/loop cycles — see Step 10).
- The role bundles (lens / voice / cadence / difficulty data) from
../../knowledge/roles.md.
Steps
Kickoff — read the context and check for a named role (F1.2). Read the working context (current directory, open/recent files, language, test state). If the user named a role, adopt it without proposing an alternative (F1.2) and continue at Step 4. Observable result: either an adopted role, or the read context handed to Step 2.
Kickoff — infer one candidate role from the signal→role map (F1.1). Match the working context against this table, top-down; take the first row whose signal holds:
| Signal (observable in the working context) |
Inferred role |
| The user named a role |
that role (adopted in Step 1 — no inference) |
| Failing tests plus a stub / TODO / unimplemented target |
interviewer |
| Feature-in-progress edits to working code, with tests present |
pair-programmer |
| Exploratory work — reasoning aloud, no tests in play |
rubber-duck |
State only the one-line reason for the inferred role (the matched signal); do not diagnose bugs or name fixes before a role is confirmed. Observable result: one named candidate role plus its one-line matched-signal reason, and no feedback emitted yet.
Kickoff — propose the candidate for confirmation (F1.1, F1.3, F1.5). Propose the inferred role via AskUserQuestion, offering the full role set (rubber-duck, pair-programmer, interviewer, custom) as the override options. Observable result: a single confirmed role, or — if the user confirms nothing — no session started and observing does not begin (F1.5).
Custom-role lens gate (F1.4). If the confirmed role is custom and no one-line lens/voice description was supplied, request one via AskUserQuestion before continuing. Observable result: a one-line custom lens/voice string captured; for non-custom roles this step is a no-op.
Write session-state note (carries mutable state across stateless /loop wakes — grounding design note). Write a small session-state note (scratchpad file) recording the active role, the custom lens line if any, the interviewer difficulty level (start: baseline), the inactivity limit (default or the kickoff override — Step 10), and a last-cycle marker (timestamp / change cursor). The /loop re-fires the same prompt each wake and does not remember state, so this note is the source of truth re-read every cycle. Observable result: a session-state file exists on disk with the active role and difficulty recorded.
Scaffold an interviewer exercise — conditional (F2.1–F2.3). Read the active role from the session-state note. If it is interviewer: if language or topic is missing, request them via AskUserQuestion first (F2.2); then Write an exercise file comprising a problem statement, a function placeholder, and a runnable test suite, and confirm it fails on first run (F2.1). If the active role is not interviewer, generate no exercise even if asked (F2.3): decline, state the reason (scaffolding is interviewer-only), and offer a switch to interviewer via AskUserQuestion; if the user switches, record the new role in the session-state note and fall through to this step's language/topic gate (F2.2) before scaffolding. Observable result: for interviewer, an exercise file exists and its test suite is red on first run; for any other role, no exercise file is written and any scaffold request got a decline plus a switch offer.
Start the observe loop (F3.1). Start the substrate's self-paced /loop (omit the interval). Do not build a scheduler (../../../.lsa/VISION.md principle 9). Observable result: a self-paced /loop is running with this Actor as its per-cycle prompt.
Per cycle — read changes, apply the active role's bundle (F3.1–F3.5). On each wake: (a) re-read the session-state note to recover the active role + difficulty; (b) read the file changes since the last-cycle marker; (c) read that role's bundle from ../../knowledge/roles.md and apply its lens (in the bundle's order), voice, and cadence generically — this Actor selects no behavior inline; (d) emit feedback, or stay silent when the bundle's cadence is quiet and the changes hold no genuine catch — silence per ../../knowledge/roles.md §"Silence — the zero-output rule". A silent cycle produces zero output and simply ends after step (e); (e) update the last-cycle marker (and, for interviewer, the difficulty level) in the session-state note per the bundle's difficulty rules. Observable result: each cycle either emits feedback shaped by the active bundle or emits nothing, and the session-state note's last-cycle marker (and interviewer difficulty) is updated. The bundle drives whether pair-programmer stays silent (F3.2), whether it consults the wider project before flagging (F3.3), the interviewer's solution→bugs→performance→style ordering with non-breaking safer-alternative phrasing (F3.4), and the interviewer's cross-cycle difficulty adaptation (F3.5).
Role-switch mid-session (F4.1). When the user requests a different role during the session, update the active role (and custom lens, if custom) in the session-state note; the change takes effect on the next cycle, which reads the new bundle. Do not restart the loop. Observable result: the session-state note records the new active role and the next cycle's feedback follows the new bundle with no loop restart.
Stop (F5.1–F5.2). End the /loop when the user requests stop (F5.1), when the session is self-determined over, or when inactivity reaches the inactivity limit (F5.2): 2 consecutive /loop cycles with no file changes since the last-cycle marker — cycle-based because the loop is self-paced (no wall clock). That is the default; the escape hatch is a user override at kickoff (e.g. "keep watching for 5 quiet cycles"), recorded in the session-state note and read from there. On stop, report the reason. Observable result: the loop is ended and a one-line stop reason is reported (user request, concluded, or inactivity limit reached — with the cycle count).
Output
A live observe session: a confirmed role and (for interviewer) a red exercise file at kickoff; per-cycle feedback or silence shaped by the active role's bundle; on role-switch the next cycle follows the new bundle without a restart; on stop the loop ends with a stated reason. The session-state note is the persistent record of active role + interviewer difficulty across cycles.
Example Output
[illustrative]
=============== [observer/skills/observe/SKILL.md] [observer] ===============
Kickoff — context: a Python file with a `# TODO` stub and a failing pytest, no role named.
Inferred: interviewer — signal matched: failing tests plus a stub.
Proposed role: interviewer (override: rubber-duck / pair-programmer / custom) > confirmed
Language/topic? > Python / binary search
Wrote exercise: scratchpad/exercise_binary_search.py + test — 3 tests RED on first run.
Session-state: role=interviewer difficulty=baseline (scratchpad/observe-state.md)
[loop started — self-paced]
cycle 1 — change: implemented bisect loop.
bugs: off-by-one — `hi = mid` re-tests a midpoint you've already excluded (the
approach is right; this is a defect within it). Safer direction: move the bound that
excludes the checked midpoint — which bound, and by how much? (you write the line).
cycle 2 — no file changes since the last-cycle marker (1st consecutive quiet cycle).
cycle 3 — no file changes (2nd consecutive — inactivity limit reached).
Stopped: inactivity limit (2 consecutive no-change /loop cycles — default; overridable at kickoff).
Constraints
- No per-role branch logic. This Actor never hard-codes a role's lens/voice/cadence or difficulty thresholds inline — it reads the active bundle from
../../knowledge/roles.md and applies it generically (SoC invariant, ../../../.lsa/VISION.md principle 4).
- No scheduler. Ride the substrate's self-paced
/loop; do not implement polling, timers, or a wake scheduler (../../../.lsa/VISION.md principle 9).
- No observing without a confirmed role (F1.5). Kickoff must resolve to one confirmed role before Step 7, and pre-confirmation output is bounded to the one-line matched-signal reason (Step 2) — no diagnosing bugs or naming fixes before the role is confirmed.
- Scaffold is interviewer-only (F2.3). Never write an exercise in a non-interviewer role.
- State lives in the session-state note, not in loop memory. The
/loop is stateless between wakes; the active role and interviewer difficulty are re-read from the note each cycle and written back on change.
- Non-destructive feedback. Emit feedback as explanation; do not edit the user's working files to "fix" them. In the interviewer role, state the gotcha and the direction of a safer alternative — do not write the corrected code or a copy-paste-ready fix line; the candidate must still write it.
- Outputs follow
../../../core/skills/output/SKILL.md — citation by link, never restated; trace line on load.
1---2name: observe3description: Start a live observe-and-coach session that rides Claude Code's self-paced /loop and reacts to your file changes through a chosen role. Use when the user says observe me / watch me code / rubber-duck this / pair with me / interview me, or asks for live feedback while they work. Confirms a role first (rubber-duck, pair-programmer, interviewer, custom), optionally scaffolds an interviewer exercise, then emits role-appropriate feedback (or silence) each cycle until stopped. Reads all per-role lens/voice/cadence as data from observer/knowledge/roles.md — never hard-codes role behavior.4---56> **Trace.** On load, print first: `=============== [observer/skills/observe/SKILL.md] [observer] ===============`789# Observe1011Orchestrator Actor. Rides the substrate's self-paced `/loop` (omit the interval to self-pace) and, on each wake, reads the file changes since the previous cycle and emits feedback through the **active role**. All role behavior — lens, voice, cadence, difficulty rules — is **data read from** [`../../knowledge/roles.md`](../../knowledge/roles.md); this Actor applies whichever bundle is active generically and contains no per-role branch logic. It rides the existing `/loop`; it does not build a scheduler ([`../../../.lsa/VISION.md`](../../../.lsa/VISION.md) principle 9, *Substrate-native first*).1213## Goal1415From a confirmed role, run a live observe-and-coach session over the user's working changes — emitting feedback that matches the active role's lens/voice/cadence each `/loop` cycle — until a stop trigger ends the loop with a stated reason.1617## Input1819- The working context (current directory, open/recent files, language). Used to infer a candidate role.20- An optional role named by the user (rubber-duck, pair-programmer, interviewer, custom).21- For a custom role: a one-line lens/voice description.22- For an interviewer role: a target language and topic for the exercise.23- An optional inactivity-limit override (default: 2 consecutive no-change `/loop` cycles — see Step 10).24- The role bundles (lens / voice / cadence / difficulty data) from [`../../knowledge/roles.md`](../../knowledge/roles.md).2526## Steps27281. **Kickoff — read the context and check for a named role** (F1.2). Read the working context (current directory, open/recent files, language, test state). If the user named a role, adopt it without proposing an alternative (F1.2) and continue at Step 4. Observable result: either an adopted role, or the read context handed to Step 2.29302. **Kickoff — infer one candidate role from the signal→role map** (F1.1). Match the working context against this table, top-down; take the first row whose signal holds:3132 | Signal (observable in the working context) | Inferred role |33 |---|---|34 | The user named a role | that role (adopted in Step 1 — no inference) |35 | Failing tests plus a stub / TODO / unimplemented target | interviewer |36 | Feature-in-progress edits to working code, with tests present | pair-programmer |37 | Exploratory work — reasoning aloud, no tests in play | rubber-duck |3839 State only the one-line reason for the inferred role (the matched signal); do not diagnose bugs or name fixes before a role is confirmed. Observable result: one named candidate role plus its one-line matched-signal reason, and no feedback emitted yet.40413. **Kickoff — propose the candidate for confirmation** (F1.1, F1.3, F1.5). Propose the inferred role via `AskUserQuestion`, offering the full role set (rubber-duck, pair-programmer, interviewer, custom) as the override options. Observable result: a single confirmed role, or — if the user confirms nothing — no session started and observing does not begin (F1.5).42434. **Custom-role lens gate** (F1.4). If the confirmed role is custom and no one-line lens/voice description was supplied, request one via `AskUserQuestion` before continuing. Observable result: a one-line custom lens/voice string captured; for non-custom roles this step is a no-op.44455. **Write session-state note** (carries mutable state across stateless `/loop` wakes — grounding design note). `Write` a small session-state note (scratchpad file) recording the active role, the custom lens line if any, the interviewer difficulty level (start: baseline), the inactivity limit (default or the kickoff override — Step 10), and a last-cycle marker (timestamp / change cursor). The `/loop` re-fires the same prompt each wake and does not remember state, so this note is the source of truth re-read every cycle. Observable result: a session-state file exists on disk with the active role and difficulty recorded.46476. **Scaffold an interviewer exercise — conditional** (F2.1–F2.3). Read the active role from the session-state note. If it is interviewer: if language or topic is missing, request them via `AskUserQuestion` first (F2.2); then `Write` an exercise file comprising a problem statement, a function placeholder, and a runnable test suite, and confirm it **fails on first run** (F2.1). If the active role is not interviewer, generate no exercise even if asked (F2.3): decline, state the reason (scaffolding is interviewer-only), and offer a switch to interviewer via `AskUserQuestion`; if the user switches, record the new role in the session-state note and fall through to this step's language/topic gate (F2.2) before scaffolding. Observable result: for interviewer, an exercise file exists and its test suite is red on first run; for any other role, no exercise file is written and any scaffold request got a decline plus a switch offer.48497. **Start the observe loop** (F3.1). Start the substrate's self-paced `/loop` (omit the interval). Do not build a scheduler ([`../../../.lsa/VISION.md`](../../../.lsa/VISION.md) principle 9). Observable result: a self-paced `/loop` is running with this Actor as its per-cycle prompt.50518. **Per cycle — read changes, apply the active role's bundle** (F3.1–F3.5). On each wake: (a) re-read the session-state note to recover the active role + difficulty; (b) read the file changes since the last-cycle marker; (c) read that role's bundle from [`../../knowledge/roles.md`](../../knowledge/roles.md) and apply its lens (in the bundle's order), voice, and cadence **generically** — this Actor selects no behavior inline; (d) emit feedback, or stay silent when the bundle's cadence is quiet and the changes hold no genuine catch — silence per [`../../knowledge/roles.md`](../../knowledge/roles.md) §"Silence — the zero-output rule". A silent cycle produces zero output and simply ends after step (e); (e) update the last-cycle marker (and, for interviewer, the difficulty level) in the session-state note per the bundle's difficulty rules. Observable result: each cycle either emits feedback shaped by the active bundle or emits nothing, and the session-state note's last-cycle marker (and interviewer difficulty) is updated. The bundle drives whether pair-programmer stays silent (F3.2), whether it consults the wider project before flagging (F3.3), the interviewer's solution→bugs→performance→style ordering with non-breaking safer-alternative phrasing (F3.4), and the interviewer's cross-cycle difficulty adaptation (F3.5).52539. **Role-switch mid-session** (F4.1). When the user requests a different role during the session, update the active role (and custom lens, if custom) in the session-state note; the change takes effect on the next cycle, which reads the new bundle. Do not restart the loop. Observable result: the session-state note records the new active role and the next cycle's feedback follows the new bundle with no loop restart.545510. **Stop** (F5.1–F5.2). End the `/loop` when the user requests stop (F5.1), when the session is self-determined over, or when inactivity reaches the **inactivity limit** (F5.2): **2 consecutive `/loop` cycles with no file changes** since the last-cycle marker — cycle-based because the loop is self-paced (no wall clock). That is the default; the escape hatch is a user override at kickoff (e.g. "keep watching for 5 quiet cycles"), recorded in the session-state note and read from there. On stop, report the reason. Observable result: the loop is ended and a one-line stop reason is reported (user request, concluded, or inactivity limit reached — with the cycle count).5657## Output5859A live observe session: a confirmed role and (for interviewer) a red exercise file at kickoff; per-cycle feedback or silence shaped by the active role's bundle; on role-switch the next cycle follows the new bundle without a restart; on stop the loop ends with a stated reason. The session-state note is the persistent record of active role + interviewer difficulty across cycles.6061## Example Output6263[illustrative]6465```66=============== [observer/skills/observe/SKILL.md] [observer] ===============6768Kickoff — context: a Python file with a `# TODO` stub and a failing pytest, no role named.69Inferred: interviewer — signal matched: failing tests plus a stub.70Proposed role: interviewer (override: rubber-duck / pair-programmer / custom) > confirmed71Language/topic? > Python / binary search72Wrote exercise: scratchpad/exercise_binary_search.py + test — 3 tests RED on first run.73Session-state: role=interviewer difficulty=baseline (scratchpad/observe-state.md)7475[loop started — self-paced]7677cycle 1 — change: implemented bisect loop.78 bugs: off-by-one — `hi = mid` re-tests a midpoint you've already excluded (the79 approach is right; this is a defect within it). Safer direction: move the bound that80 excludes the checked midpoint — which bound, and by how much? (you write the line).81cycle 2 — no file changes since the last-cycle marker (1st consecutive quiet cycle).82cycle 3 — no file changes (2nd consecutive — inactivity limit reached).8384Stopped: inactivity limit (2 consecutive no-change /loop cycles — default; overridable at kickoff).85```8687## Constraints8889- **No per-role branch logic.** This Actor never hard-codes a role's lens/voice/cadence or difficulty thresholds inline — it reads the active bundle from [`../../knowledge/roles.md`](../../knowledge/roles.md) and applies it generically (SoC invariant, [`../../../.lsa/VISION.md`](../../../.lsa/VISION.md) principle 4).90- **No scheduler.** Ride the substrate's self-paced `/loop`; do not implement polling, timers, or a wake scheduler ([`../../../.lsa/VISION.md`](../../../.lsa/VISION.md) principle 9).91- **No observing without a confirmed role** (F1.5). Kickoff must resolve to one confirmed role before Step 7, and pre-confirmation output is bounded to the one-line matched-signal reason (Step 2) — no diagnosing bugs or naming fixes before the role is confirmed.92- **Scaffold is interviewer-only** (F2.3). Never write an exercise in a non-interviewer role.93- **State lives in the session-state note, not in loop memory.** The `/loop` is stateless between wakes; the active role and interviewer difficulty are re-read from the note each cycle and written back on change.94- **Non-destructive feedback.** Emit feedback as explanation; do not edit the user's working files to "fix" them. In the interviewer role, state the gotcha and the *direction* of a safer alternative — do not write the corrected code or a copy-paste-ready fix line; the candidate must still write it.95- Outputs follow [`../../../core/skills/output/SKILL.md`](../../../core/skills/output/SKILL.md) — citation by link, never restated; trace line on load.