Coaching — what gets observed, and what the student is told
The design for issue #167. The decisions and their evidence are in
ADR 0004; the workings are in
research/. This document is the shape of what gets built.
The one-sentence version
Two delivery points over one set of deterministic observations: a single line at git push about
what already happened, and a Socratic preamble injected before it happens.
What is observed
Everything comes from data already captured. No new collection, no model, no BYOK, no adapter work.
| Signal | Source | Strength |
|---|---|---|
| No pull request for a pushed branch | command log (gh pr create) + branch state |
Grounded — ADR 0004 §2 |
| Commit message states what but not why | git commit -m in the command log |
Grounded — ADR 0004 §3 |
| AI code accepted with no subsequent run, test or build | existing Verification signal (§6), R7.3.3 | Grounded — ADR 0004 §4 |
| Share of final code that came from AI | existing Reliance signal (§6) | Grounded — ADR 0004 §4 |
| Commit granularity | commits per push, files per commit | Inference. Weakest of the set; carried, marked, and dropped if it proves noisy |
Deliberately absent: working directly on main. Contradicted by primary evidence (ADR 0004 §1).
Two rules borrowed from the harness rather than invented:
- R6.1 applies. Every line cites what produced it. "Three commits, none saying why" — not "improve your commit hygiene." Unexplained advice invites arguments nobody can settle.
- R6.7's sibling. Nothing here says whether the code works. The autograder owns that, and conflating them makes good process on a failing solution look like failure.
Delivery 1 — the push-time line
Rides the existing pre-push hook and notice log. No new command, so G6 holds: a student who never learns a cin90 command still gets this.
Three constraints it inherits, all load-bearing:
- One line, printed verbatim (R7.5b.9). It enters as a notice kind, so
noticeLineflattens and the hook prints at most one. - Lowest urgency in the queue.
noticeUrgencyalready ranks what a student most needs to hear. A caught credential (R8.11) or a failed submit outranks coaching every time, so on a busy push the line is silently dropped. Correct: a rotation-worthy secret must never queue behind a note about commit messages. - Never blocks, never prompts (R7.5b.9). The hook is a capture trigger, not a gate.
One signal per push, highest-value first. A student who gets the same line three pushes running learns nothing on the second and third, so a line is suppressed after it has been shown for the same finding within a session.
Delivery 2 — instruction-file injection
R7.3.1: a Socratic preamble written into the AI CLI's own project instruction file, disclosed to the student (R7.3.2 forbids claiming guidance is active when it is not).
This is the half the evidence actually supports — every documented countermeasure to a disengaged pair partner is a structural role rotation, not a report afterwards (ADR 0004 §5). Its job is to keep the student in the driving seat, which the N=587 result identifies as the mediator that matters.
It degrades loudly. Injection is per-tool and fragile. Claude Code does not read AGENTS.md
and documents that it does not, so there is no single file that reaches every tool. Where injection
is unsupported the tool says so and falls back to advisory — it never claims guidance is on.
Policy
Instructor-defined and server-resolved, the same shape as rubric weights (R6.6) and the redaction
toggle (R8.12). guided_mode already exists in the contract with enabled and
guidance_override; the commit convention is a new field, declared x-cin90-status: planned
because no server serves it yet.
That tag is documentation here, not a gate, and the difference is worth knowing before you go
looking for the enforcement. x-cin90-status is machine-enforced on operations — the guard
refuses the call — and a field cannot be refused, because the server simply does not send it. What
keeps the CLI honest is that the field is optional and its absence renders as absence: cin90 policy says the server does not serve one. It never silently substitutes the default, which would
manufacture an instructor decision out of an unimplemented field.
Default when unset: Conventional Commits — as the served format only, and only once a server resolves the field at all. Compliance is never scored (ADR 0004 §3).
Per R6.5, the effective convention renders in cin90 policy. A student must be able to read
the standard before a push-time line measures them against it.
What would tell us this is wrong
Written down now, because it is the failure this feature is most likely to produce and the hardest to notice from inside:
- Students' commit messages become compliant and stay uninformative. That is the 50,673-commit finding reproducing in our own data, and it means the signal is grading syntax.
- Coaching lines correlate with better submitted artifacts but not with individual assessment. That is 86.6%-vs-68.1% and 75.2%-vs-74.4% — better artifacts, no transfer (ADR 0004, Consequences).
- Students dismiss or route around the line rather than acting on it. Nudges must be rate-limited and dismissible (R7.3.4), and a dismissal rate that climbs is the signal that the line has become noise.
Not in scope
- Scoring correctness — the autograder's job (R6.7).
- Detecting browser AI use — uncapturable, and NG1 says so in-product.
- Blocking a push for any reason. The hook fails open, always.
Read this page in your terminal: install the cin90 CLI and run
cin90 docs. The same page is served as
raw markdown.