docs: tighten hourlog, add the confirmed-entry check
Scan and report had grown into two sections saying the same thing, and
step 4 repeated step 1's "show the table". Down to four steps.
Adds what nearly went wrong on the first real run: a day already
confirmed weeks ago looks identical to a planned one in a bulk approval,
and overwriting it needs its own yes. Also records the 15-minute
rounding the app enforces, and the ${VAR:-x} expansion that prints a
token instead of hiding it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+61
-74
@@ -9,126 +9,113 @@ allowed-tools:
|
||||
- Glob
|
||||
- Bash
|
||||
- Edit
|
||||
- Write
|
||||
---
|
||||
|
||||
# Hour log
|
||||
|
||||
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.
|
||||
Report how long each client project was 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
|
||||
the proposal.
|
||||
the table.
|
||||
|
||||
## Setup (once per machine)
|
||||
|
||||
Three things must exist. Check them before anything else and stop with the
|
||||
missing step if not.
|
||||
Check all three before anything else; stop with the missing step if not.
|
||||
|
||||
1. `~/.config/hourlog/projects.json` — path prefix to project mapping. Copy
|
||||
`<skill-dir>/config.example.json` and fill it in. **Never commit a filled
|
||||
config, and never put a project, client, or host name in this repo** — it
|
||||
is public.
|
||||
1. `~/.config/hourlog/projects.json` — path prefix to project mapping, copied
|
||||
from `<skill-dir>/config.example.json`. **Never commit a filled config, and
|
||||
never put a project, client, or host name in this repo** — it is public.
|
||||
2. `HOURLOG_API` and `HOURLOG_TOKEN` in `~/.env.claude`. The token is a
|
||||
personal access token from the timesheet app's profile page. Scopes:
|
||||
`profile:read`, `schedule:read`, and `schedule:write` only if submitting.
|
||||
Never echo the token.
|
||||
3. Project names in the config must match the app exactly. Verify with
|
||||
personal access token from the timesheet app's profile page: `profile:read`,
|
||||
`schedule:read`, and `schedule:write` only if submitting. Never echo it —
|
||||
`${VAR:-x}` prints the value when the variable is set; use `${VAR:+set}`.
|
||||
3. Config project names must match the app exactly. Verify with
|
||||
`me-api.py projects` and fix the config, not the app.
|
||||
|
||||
## 1. Scan the sessions
|
||||
## 1. Scan
|
||||
|
||||
```sh
|
||||
python3 <skill-dir>/scripts/scan-activity.py --week last
|
||||
```
|
||||
|
||||
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.
|
||||
A markdown table, one row per day, one column per project. Paste it into the
|
||||
reply as plain markdown — never in a code fence, which shows raw pipes instead
|
||||
of a rendered table — and don't restate it in prose.
|
||||
|
||||
Overlap is divided, not double-counted: five minutes with two projects open is
|
||||
five minutes of the day, half to each. The project columns therefore add up to
|
||||
`total`, which is wall-clock presence.
|
||||
What the numbers mean, and their limits:
|
||||
|
||||
That division is also the ratio to submit with. The user gives the day's real
|
||||
total; you split it by these proportions. Never submit the measured numbers as
|
||||
the hours unless the user says they are right.
|
||||
- The unit is a 5-minute slot containing at least one message, deduplicated
|
||||
per project, so a 40-subagent swarm counts once. Overlapping minutes are
|
||||
split evenly between the projects live in them, so the columns add up to
|
||||
`total`, which is wall-clock presence.
|
||||
- **Measured time is a floor, never a total.** Meetings, review, reading and
|
||||
thinking leave no transcript. Say so; the user adds them back.
|
||||
- **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. Days start at 06:00 and
|
||||
run past midnight, and a guessed number is worse than a small true one
|
||||
because the user cannot tell it was guessed.
|
||||
|
||||
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.
|
||||
|
||||
The unit behind it is a 5-minute slot in which at least one message was
|
||||
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.
|
||||
|
||||
`--json` carries the same fields if you need to compute against them.
|
||||
|
||||
## 2. Report what was measured
|
||||
|
||||
**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.
|
||||
|
||||
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.
|
||||
|
||||
Where judgement genuinely belongs is the `flags` column:
|
||||
Two flags need a judgement call:
|
||||
|
||||
- **`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.
|
||||
Name it so the user can discount it; never 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.
|
||||
Either a client directory the config is missing, or personal work belonging
|
||||
in `exclude`. Ask; never guess it into a client project.
|
||||
|
||||
## 3. Reconcile against the timesheet
|
||||
`--json` carries the same fields for computing against.
|
||||
|
||||
## 2. Reconcile
|
||||
|
||||
```sh
|
||||
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 carrying an entry
|
||||
id and planned hours — or absent, which needs a new entry instead of a
|
||||
Each day is either planned — an allocation carrying an entry id, planned
|
||||
hours, and a status — or absent, which needs a new entry rather than a
|
||||
confirmation.
|
||||
|
||||
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.
|
||||
**Check the status of every entry before proposing a value.** `planned` with
|
||||
`actual_hours: null` is untouched and safe to fill. `confirmed` or `edited`
|
||||
means the user already set that number, possibly weeks ago. Overwriting one is
|
||||
a separate decision: show the current value against the proposed value, say
|
||||
which day it is, and get a specific yes for that entry. Do not fold it into a
|
||||
bulk approval.
|
||||
|
||||
## 4. Show the table, then ask
|
||||
Show planned hours beside measured ones and let the user set the number. The
|
||||
measured figure is almost always lower, and that gap is work off the keyboard,
|
||||
not evidence the plan is wrong.
|
||||
|
||||
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.
|
||||
## 3. Ask
|
||||
|
||||
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.
|
||||
The table, plus one line for anything flagged. No commentary on days that were
|
||||
straightforward, and no 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
|
||||
say the run is waiting.
|
||||
Then ask once, plainly, whether to submit. Wait. Silence, a timeout, or "user
|
||||
may be away" is not approval — leave the timesheet alone and say the run is
|
||||
waiting.
|
||||
|
||||
## 5. Submit what was approved
|
||||
## 4. Submit
|
||||
|
||||
```sh
|
||||
# confirm a planned day
|
||||
python3 <skill-dir>/scripts/me-api.py confirm --entry ID --hours 8 --dry-run
|
||||
python3 <skill-dir>/scripts/me-api.py confirm --entry ID --hours 5.25 --dry-run
|
||||
# log a day with no allocation
|
||||
python3 <skill-dir>/scripts/me-api.py log --project ID --dates D,D --hours 8 --dry-run
|
||||
python3 <skill-dir>/scripts/me-api.py log --project ID --dates D,D --hours 4 --dry-run
|
||||
```
|
||||
|
||||
Hours are decimal and the app enforces 15-minute steps, so round to a multiple
|
||||
of 0.25 before sending. When the user gives a day's total rather than
|
||||
per-project numbers, split it by the measured proportions.
|
||||
|
||||
Run every write with `--dry-run` first and show the requests. Drop the flag
|
||||
only for the rows the user approved — not the whole table, if they approved
|
||||
part of it.
|
||||
only for the rows approved — not the whole table, if they approved part of it.
|
||||
|
||||
A `423` means the period is locked and ops has to reopen it. Report it and
|
||||
move on; it is not a failure to retry.
|
||||
|
||||
Re-read the schedule afterwards and confirm what landed. Report the diff, not
|
||||
an assumption.
|
||||
A `423` means the period is locked and ops has to reopen it; report it and move
|
||||
on. Re-read the schedule afterwards and report what actually landed, not what
|
||||
was sent.
|
||||
|
||||
## Scope
|
||||
|
||||
|
||||
Reference in New Issue
Block a user