feat: report measured time, drop the nominal-day fit
Splitting a fixed 8h day by share turned 160 minutes of Friday morning into "7h Tesser". The number looked measured and was not, and no column in the table said which. Cells now carry the time each project was actually active. The total column is wall-clock presence — the union of active slots — so it reads lower than the row sum when sessions overlapped. Measured time is a floor; the skill says so and leaves adding the rest to the user. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+32
-42
@@ -14,8 +14,8 @@ allowed-tools:
|
||||
|
||||
# Hour log
|
||||
|
||||
Turn session activity into an hours-per-project-per-day proposal, check it against
|
||||
the timesheet, and submit only what the user approves.
|
||||
Report how long each client project was actually active on each day, check
|
||||
that against the timesheet, and submit only the hours the user gives you.
|
||||
|
||||
The timesheet is a company record. Nothing is written to it without the user
|
||||
saying go, in this session, after seeing the table. An unattended run stops at
|
||||
@@ -43,9 +43,13 @@ missing step if not.
|
||||
python3 <skill-dir>/scripts/scan-activity.py --week last
|
||||
```
|
||||
|
||||
Prints a markdown table: one row per day, one column per project holding
|
||||
`hours · share · active minutes`, then a totals row. That table is the
|
||||
deliverable — paste it as-is rather than restating it in prose.
|
||||
Prints a markdown table: one row per day, one column per project holding the
|
||||
time that project was actually active, plus wall-clock `total`, the day's
|
||||
`window`, and `flags`. That table is the deliverable — paste it as-is rather
|
||||
than restating it in prose.
|
||||
|
||||
`total` is the union of active slots, so on a day with parallel sessions it is
|
||||
less than the row sum. Both numbers are true; they answer different questions.
|
||||
|
||||
Paste it as plain markdown in the reply. Never wrap it in a code fence: a
|
||||
fence shows the raw pipes and dashes instead of a rendered table.
|
||||
@@ -55,37 +59,26 @@ written, deduplicated per project — so a 40-subagent swarm on one project
|
||||
counts once, and two projects worked in parallel each keep their own slots.
|
||||
Message counts would let one overnight autonomous run outweigh a real morning.
|
||||
|
||||
The `flags` column carries the two things that need judgement, not arithmetic:
|
||||
|
||||
- **`outside HHh`.** Activity at 02:00 is usually an unattended run, not work.
|
||||
If most of a project's minutes came from 21h-02h, its share is inflated —
|
||||
say so and shift the split.
|
||||
- **`unmapped time excluded`.** A path with no rule, left out of the split, so
|
||||
that day is wrong rather than merely incomplete. Either it is a new client
|
||||
directory the config is missing — say so and ask — or it is personal work
|
||||
that belongs in `exclude`. Never guess it into a client project.
|
||||
|
||||
`--json` carries the same fields if you need to compute against them.
|
||||
|
||||
## 2. Propose the hours
|
||||
## 2. Report what was measured
|
||||
|
||||
**Active minutes are a floor on real work, never a measure of it.** Meetings,
|
||||
review, reading, whiteboarding, and thinking leave no transcript at all. A day
|
||||
showing 160 active minutes was not a two-hour day. Read the numbers as
|
||||
*proportions between projects*, and get the day's length from the calendar.
|
||||
**Do not extrapolate.** No fitting to an 8-hour day, no scaling a thin day up,
|
||||
no rounding a dominant project to the whole day. Some days start at 06:00,
|
||||
some run past midnight, and none of them are the same length. A guessed number
|
||||
is worse than a small true one, because the user cannot tell it was guessed.
|
||||
|
||||
The scan already does the arithmetic: it splits `nominal_day_hours` (config,
|
||||
default 8) across the day's mapped projects by share, in 15-minute steps. Your
|
||||
job is the judgement it cannot do:
|
||||
State the measured time and say plainly that it is a floor: meetings, review,
|
||||
reading and thinking leave no transcript, so real hours are higher by an
|
||||
amount only the user knows. They add that back — you never do.
|
||||
|
||||
- One project at 85% or more: round it up to the whole day rather than leaving
|
||||
a token 1h on the other.
|
||||
- Days flagged `under 30min, treat as empty` get nothing proposed.
|
||||
- Correct the split for the two things flagged in step 1 before proposing it.
|
||||
Where judgement genuinely belongs is the `flags` column:
|
||||
|
||||
Never scale a day *down* because its transcripts are thin. A quiet day is a
|
||||
normal working day unless the user says it was not, or the timesheet already
|
||||
marks it as time off or a holiday.
|
||||
- **`outside HHh`** — time at 02:00 is usually an unattended run, not work.
|
||||
Name it so the user can discount it; do not silently drop it.
|
||||
- **`unmapped time excluded`** — a path with no rule, left out of that day.
|
||||
Either it is a new client directory the config is missing, or it is personal
|
||||
work belonging in `exclude`. Ask; never guess it into a client project.
|
||||
|
||||
## 3. Reconcile against the timesheet
|
||||
|
||||
@@ -93,24 +86,21 @@ marks it as time off or a holiday.
|
||||
python3 <skill-dir>/scripts/me-api.py schedule --start YYYY-MM-DD --end YYYY-MM-DD
|
||||
```
|
||||
|
||||
Each day comes back either already planned (an allocation with an entry id and
|
||||
planned hours) or absent. That splits the proposal in two:
|
||||
Each day comes back either already planned — an allocation carrying an entry
|
||||
id and planned hours — or absent, which needs a new entry instead of a
|
||||
confirmation.
|
||||
|
||||
- **Planned and matching** — confirm at the planned hours.
|
||||
- **Planned but the sessions disagree** — confirm at the observed hours, and
|
||||
show both numbers in the table so the user sees what changed.
|
||||
- **Not planned at all** — needs a new entry, which is a bigger claim. Flag it
|
||||
separately rather than folding it in.
|
||||
Show the planned hours next to the measured ones and let the user set the
|
||||
number. The measured figure is almost always lower, and that gap is real work
|
||||
off the keyboard, not evidence that the plan is wrong.
|
||||
|
||||
## 4. Show the table, then ask
|
||||
|
||||
One row per project per day: date, weekday, project, proposed hours, what the
|
||||
timesheet says now, and the action (confirm / adjust / add / skip). Mark any
|
||||
The scan's table with the timesheet's planned hours beside each cell. Mark any
|
||||
day that needs a new entry rather than a confirmation.
|
||||
|
||||
Keep it to that table plus a line for anything you had to judge — a discounted
|
||||
overnight run, an unmapped path, a day you left empty. No commentary on days
|
||||
that were straightforward.
|
||||
Keep it to that table plus a line for anything flagged. No commentary on days
|
||||
that were straightforward, and no proposed totals the user did not give you.
|
||||
|
||||
Then ask once, plainly, whether to submit. Wait for an answer. Silence, a
|
||||
timeout, or "user may be away" is not approval — leave the timesheet alone and
|
||||
|
||||
Reference in New Issue
Block a user