agentleFS
BryceEWatsonBryceEWatson/claude-global-skills/weekly-work-log/SKILL.md

weekly-work-log

Skill0 starsChanged 44 days agoMITView on GitHub

Build and refresh the brycewatson.com "Weekly Work Log" report page (/weekly-report, archived under /log) for the last completed week. Discovers the week's work from session-end handoffs + git, distils it into honest public-voice items, re-derives and verifies every number, and (unattended, every Sunday night via a Claude scheduled task) opens one PR that merges itself only through a merge gate (Owner ruling 2026-09-10: every deterministic gate green and no advisory flag), plus a LinkedIn candidate and a post card that never publish themselves.

  • Commits and pushes

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Distinctive:

    Before 2026-09-17 an open PR was always a success, which is how PR 112 blocked four weeks unseen.

    line 59
  2. Very specific:

    Failure visibility: every run starts by writing last-run.json under the live scheduled-task directory and must write one terminal state.

    line 119
  3. Very specific:

    Never push to main; never deploy.

    line 141
  4. Distinctive:

    Never mutate the user's shared checkout.

    line 142

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Weekly Work Log

Builds and refreshes the Weekly Work Log at brycewatson.com/weekly-report: a per-project feed of the specific work Bryce did last week, each item with an honest status badge and an expandable evidence drawer, a git-derived commits/day chart for the week, and per-project archive pages. Each week's report is snapshotted; brycewatson.com/log is the index of all past weekly reports.

Routes: /weekly-report (live, current week) · /weekly-report/<project> (per-project commit-history archive) · /log (index of weekly reports) · /log/<week> (an archived week).

Target project: C:\Users\Bryce\Projects\brycewatson.com. The page + build scripts live there; this skill is the reusable orchestrator + the weekly scheduler.

The window: the last completed week

The report always covers the most recently completed Monday–Sunday week (a week completes on Sunday). Run on a Sunday night, that is the week that just ended; run any other day, it is the previous full week. Discovery (handoffs + git), the chart, and the per-project metrics all use this same week, resolved by the single shared resolveWeek() in scripts/lib/work-log-sessions.mjs. It is anchored on the calendar week, never a rolling window walking back from today.

Backfill (--week YYYY-MM-DD) regenerates a PAST completed week's /log archive (snapshot + index + re-aggregated goals) and leaves the live work-log.json untouched. Any day inside the target week resolves to that whole Mon–Sun window; a future date or the current in-progress week aborts. Backfill is opt-in / operator-confirmed and regenerates exactly one completed week per invocation (blast radius bounded to one PR); it is never fired by the scheduled run (the unattended agent always targets the last completed week — one PR, not N).

What runs where

Everything runs LOCALLY (it reads ~/.claude handoffs + the sibling repos under C:\Users\Bryce\Projects\); GitHub Pages CI cannot. The committed JSON is what CI builds, exactly like art.json/books.json.

  • <repo>/src/data/work-log.source.json — approval-gated content (the human gate): Claude distills it from the week's handoffs + redacted session digest + git; you review and approve the PR; the generator verifies every number. Prose is distilled and human-reviewed, not number-verified.
  • <repo>/src/data/work-log.json — GENERATED + committed (every number re-derived + git-verified).
  • <repo>/src/data/reports/<weekStart>.json + reports/index.json — committed per-week snapshots (the /log archive series); the generator writes these.
  • <repo>/src/data/goals.json — GENERATED + committed (the /goals cross-week aggregation).
  • <repo>/src/components/ReportPanel.astro — the report panel, shared by /weekly-report and /log/<week>.
  • <repo>/src/pages/weekly-report.astro + weekly-report/[project].astro + log.astro + log/[week].astro + goals.astro — the pages.
  • <repo>/scripts/draft-work-log-from-handoffs.mjs — discover (handoffs → redacted digest).
  • <repo>/scripts/draft-work-log-sessions.mjs — mine the week's interactive sessions → redacted digest.
  • <repo>/scripts/draft-work-log-proposed.mjs — ranked .proposed distil scaffold (interactive; the builder NEVER reads it).
  • <repo>/scripts/work-log-harvest-nouns.mjs — propose redactor denylist additions → gitignored sidecar (count surfaced, never raw nouns).
  • <repo>/scripts/work-log-validate-source.mjs — voice/leak gate on the DRAFTED source prose (fails loud on a dash / denylisted token / bad badge / display-role git-leak / a claiming headline with no headlineWeek stamp, a nextUp with no nextUpWeek stamp), run after distillation and before the build (also chained in pnpm build).
  • <repo>/scripts/lib/work-log-headline.mjs — the ONE decision on whether this week's headline (resolveHeadline) and nextUp (resolveNextUp) may publish; shared by both generators + the PR-body composer so they cannot drift.
  • <repo>/scripts/work-log-via-honestweek.mjs — build (the honestweek engine: verify-or-abort every commit, re-derive + number-fence every number, redact, then write work-log.json + the /log snapshot + index + goals.json). <repo>/scripts/build-work-log.mjs is retained as the parity oracle (scripts/work-log-parity.mjs proves the two agree on all four artifacts).
  • <repo>/scripts/work-log-weekly.mjs — open the review PR (--advisory <path> splices the fail-open advisory; never main, never deploy).
  • <repo>/scripts/lib/work-log-redact.mjs — shared secret/leak scrubber (the ONE canonical redactor; extend its term lists, never duplicate).
  • <repo>/scripts/work-log.mjs append <draft.json> — appends a curated batch written as a JSON file; validates first (including the apostrophe batch check) and never puts prose inside code.
  • <repo>/scripts/work-log-merge-gate.mjs — MERGE or HOLD for a weekly PR under the 2026-09-10 ruling: weekly branch, data-only allowlist, green correctness on the head, exactly one advisory-flags: 0 line, no hold marker or label. The only merge path.
  • <repo>/scripts/work-log-candidates.mjs — checks, renders, and records Bryce's labeled replies for data/weekly-candidates/<weekStart>.json (the LinkedIn candidate and the post card).
  • ~/.claude/skills/weekly-work-log/run-state.cjs — atomically records a privacy-safe running / succeeded / failed status for the Sunday run (outcomes MERGED, HELD, PR_ALREADY_OPEN, NO_CHANGE). The Monday preview reads this instead of treating every missing PR as a quiet no-op.
  • ~/.claude/skills/weekly-work-log/weekly-pr-guard.cjs — the preflight check for an open weekly or backfill PR. This week's weekly PR, opened on or after the Sunday that ends this run's week (Pacific) and still open or already merged: quiet success (PR_ALREADY_OPEN or MERGED), so a second firing never publishes the week twice; if the first firing left a HELD or PR_OPENED verdict on that same PR, the guard keeps it so Monday still asks. An open PR opened before that Sunday (anything seven or more days old, or a PR a late-starting run opened mid-week), or any open backfill PR, is a leftover. Exactly one leftover: ROLL, and the run builds this week on top of that PR's branch, opens one PR carrying both, copies over any hold label so the gate still holds it, and closes the leftover. More than one leftover, or a leftover beside this week's own PR: failure STALE_WEEKLY_PR with the PR's link, which Monday raises to Bryce. Before 2026-09-17 an open PR was always a success, which is how PR 112 blocked four weeks unseen; from 2026-09-17 to 2026-09-30 any leftover stopped the run, which is how PR 137 stopped two Sundays.

Modes

  • /weekly-work-log (interactive, a human in the loop): the full build — discover → distil candidates → build → preview. New items get WRITTEN here (judgment). The optional .proposed scaffold (draft-work-log-proposed.mjs) gives you a ranked, mirror-shaped starting point to fill in the public voice; you then MOVE accepted items by hand into source.json (the gate).
  • Unattended (the Sunday-night Claude scheduled task) — curates the whole week, then opens one PR:
    • Isolated checkout: the user's brycewatson.com checkout is never switched, reset, stashed, or cleaned. The task fetches origin/main and works in the dedicated sibling worktree brycewatson.com-weekly-work-log. A clean registered stale worktree may be replaced; a dirty or unregistered directory is preserved and surfaced as a failed run.
    • Curate every session (distillation): distil EVERY interactive Claude Code session of the week in an allowlisted repo into source.json items so the page shows the full week (the original "curate every session" directive, automated; a session anywhere else never reaches the digest, see Privacy). Done in-context from the redacted digest (not subagents). Private/sensitive sessions are summarized through the privacy filter, not dropped or stubbed (display roles: Akaya/Personal/ShopForge — generalized to the kind of work, never git-read; "personal finance is fine, we just need iron clad rules"). Each item is linked to the SPECIFIC goal it advances via objectiveId (read objectives.public.json) — never leave a multi-goal project like Command to fall through to its catch-all/parent goal, or the goal lens collapses to one bucket; client/private items get no objectiveId (they stay off /goals). The drafted prose is gated by work-log-validate-source.mjs (fails loud on a dash/leak/bad-badge) + a claim-falsification self-check BEFORE the build.
    • Deterministic backstop: work-log-via-honestweek.mjs (honestweek) git-verifies + number-fences every number (aborts on any unresolved/non-Bryce commit, so NO PR on bad data). Items the job drafts carry "drafted": "auto-<date>".
    • Fail-open advisory: judgment that ASSISTS the reviewer (badge-vs-prose, coverage gaps, badge-vs-git, a downgrade-only privacy adjudicator, reversal-coverage) spliced into the PR body, plus the noun-harvester (count only). Any LLM/network/gh failure → a single "advisory unavailable" line; never blocks the PR. Advisory may only DOWNGRADE/FLAG.
    • Candidates: a LinkedIn candidate for the twelve-week test and a one-screen post card (from the week's work or the findings miner), or an explicit skip with the reason for each, in data/weekly-candidates/<weekStart>.json. Each candidate carries a receipt, Bryce's voice, a named reader and what they can do after, and honest status, or it is a skip. The run first collects last week's labeled replies from the merged PR.
    • Findings miner: PAUSED since 2026-09-30 (Bryce's go): every top pick checked had been a false positive, so the run skips honestweek mine and records miner: { ran: false }. When resumed, the run executes it (formerly the Thursday weekly-publishable-findings task, now disabled) and commits its ledger in the weekly PR, so declined findings stop coming back.
    • Gated self-merge: the run opens exactly one PR and never pushes main. It then waits for CI and runs the merge gate, which merges only under Bryce's 2026-09-10 auto ruling ("auto-merge when every deterministic gate passes and the advisory raises no flag", weekly log only). A hold waits for Bryce, and Monday raises it.
  • --week YYYY-MM-DD: backfill a past completed week's archive (see above). Manual only.
  • --days N: override the window to a rolling N-day scan (manual broader discovery).

The full flow (interactive)

  1. Discover — node scripts/draft-work-log-from-handoffs.mjs (and/or draft-work-log-sessions.mjs). Scans the week's session-end handoffs / interactive sessions across the allowlisted repos, extracts tagged claims + reversals + cited commits, redacts, writes the gitignored src/data/work-log.drafts.json digest.
  2. Curate every allowlisted session — node scripts/draft-work-log-sessions.mjs for the redacted per-session digest, then distil EVERY session in that digest into source.json items in the plain public voice (private/sensitive display-role sessions are summarized through the privacy filter, never dropped; sessions outside the allowlist never enter the digest — see the rules + the unattended prompt scheduled-task-prompt.md). draft-work-log-proposed.mjs can give a ranked scaffold to fill. Then node scripts/work-log-validate-source.mjs (fails loud on a dash/leak/bad-badge) before building. Obey every rule below; accuracy is the floor even for private.
  3. Build — node scripts/work-log-via-honestweek.mjs (the honestweek engine). Re-derives every date/number from git, rewrites commit snippets to the real subject, verify-or-aborts all cited commits, number-fences every number, redacts. Writes work-log.json (+ snapshot + index + goals.json). build-work-log.mjs is the parity oracle; node scripts/work-log-parity.mjs proves they agree.
  4. Preview — pnpm dev, open http://localhost:4321/weekly-report (archive index at /log, goals at /goals).
  5. Ship — commit the data files. A merge to main deploys via CI. (Unattended: node scripts/work-log-weekly.mjs --advisory <sidecar> --candidates <file> opens the PR, and node scripts/work-log-merge-gate.mjs --pr <n> --merge decides whether it merges.) An operator backfilling several missed weeks may run work-log-via-honestweek.mjs --week once per past week and open one consolidated PR; each invocation still regenerates exactly one week.

The rules (load-bearing — every run, especially unattended, must honor these)

  • The page headline is written fresh, for THIS week, and stamped: source.headline is the <h1> — an editorial claim about the week it sits above ("The counts were wrong, and the entries beneath them gave it away"). It is AUTHORED, never computed, so distil it from THIS week's entries like any other prose, and set headlineWeek to the Monday YYYY-MM-DD of the week being reported. Write the two fields together, always. A headline stamped for another week (or unstamped) is DROPPED by the build to the neutral title "What I worked on this week." and flagged in the PR body — it is never carried forward, because a claim about last week is not true of this one. If no week-specific headline is warranted, author the neutral title deliberately; the build never invents one for you. (This existed as a silent gap: the generators passed headline straight through, so one claim rode the weeks of 2026-07-13, -07-20 and -07-27, and each /log archive kept its own copy. See docs/work-log.md → "The page headline".)
  • The "next up" line is written fresh and stamped too: source.nextUp is the closing line under the feed, and it had the SAME defect as the headline — measured on origin/main, one line rode 2026-07-13, -07-20 and -07-27, another rode 2026-06-08 and -06-15, and a third rode 2026-06-22 and -06-29. Set nextUpWeek to the Monday YYYY-MM-DD of the week being reported, and write the two fields together, always. An unstamped or mis-stamped line is DROPPED entirely (the page renders no next-up block at all) and flagged in the PR body. Two deliberate differences from the headline: there is no neutral fallback (there is no honest generic "next up" text, and inventing one would put words in Bryce's mouth about his own plans), and re-stating an unchanged plan is legitimate — a forward-looking line may genuinely still be what is next, so it publishes as long as it is re-stamped for this week. Both fields resolve through the same scripts/lib/work-log-headline.mjs. See docs/work-log.md → "The 'next up' line".
  • Voice: plain, concrete, no marketing flourishes. Subject-led headlines — lead with the work or the finding, NOT "I"/"My" (headlines must not all start the same way or read self-focused). First-person belongs in the body. No em dashes. Never announce the page's own honesty ("honest", "keeping myself honest", "proof I don't fake it") — show it through the badges + receipts. This includes generalized/private entries: state the KIND of work concretely (like any public entry) and NEVER narrate the withholding itself ("keeping it sealed", "recording only the kind of work", "surfaced here as its own thread", "keeping it generic here", "belongs in an honest log") — model on the Akaya display-role rows.
  • Honest status badges: shipped (built, merged, verified), in progress, designed, not proven (machinery exists, no real result yet). The most interesting work is often the least finished; let it wear the badge openly. Map a handoff's [verified] claim → shipped; [derived]/[assumed]/[unverified]/[handoff-claimed] → designed-not-proven ([derived] is an inference the handoff drew, not a result it observed — it never earns shipped).
  • Every number git-verified: re-derived at build, never hardcoded. A cited commit that does not resolve (or is not Bryce's: bryceewatson@gmail.com / bryceewatson@users.noreply.github.com) aborts the build. Item dates come from the commit, not the prose.
  • Privacy (default-deny): the log covers ONLY sessions rooted in an allowlisted repo (ALLOWLIST in scripts/lib/work-log-sessions.mjs). A session anywhere else, such as an unknown folder or the Watson Standard hub, is dropped before discovery counts it: no digest entry, no row (not even a redacted one), no count on the page, and no mention in another project's row. Three places are excluded even if an allowlist entry would cover them: the hub's client folders (WatsonStandard/clients/), Open Hours, which runs the free checks (WatsonStandard/goodwill/), and client and free-check code (~/ClientWork, or WS_CLIENT_WORK_ROOT when set). That keeps the Watson Standard promises "I don't publish anything that identifies you or your app without your written OK" and, for the free usability check, "I don't publish anything from your check, even with names removed, without your OK." Every public page also drops honestweek's catch-all other bucket, so no total includes those sessions. Only Bryce's OWN repos are git-read (Command, DemandForge, claude-global-skills, brycewatson.com, honestweek). Client / no-remote repos (dropKnowledge, etc.) are never git-read or named. The shared redactor scrubs codenames (plumagedispatch), client names (dropKnowledge / castgryff), vendor + niche names, session UUIDs, home paths, and secrets. Draft-and-distil, never lift: handoff prose is operational and leaky; distil it into plain public-voice items, never paste it verbatim. The .proposed scaffold and the harvester sidecar are gitignored; never commit them, and never route raw harvested nouns into the PR body (count only).
  • Structure: the feed is grouped by project into containers; each project is a card with git-derived metrics (commits this week, active days, entries) + an archive → link to /weekly-report/<project> (6-month commit history). Dark console aesthetic; the shared site header uses headerVariant="dark-canvas".
  • The approval gate is the merge gate: the unattended run never pushes to main and never deploys. It opens a PR, and work-log-merge-gate.mjs merges it only when every deterministic gate is green and the advisory's single advisory-flags: line reads 0; anything else waits for Bryce. An advisory that did not run is a flag, not a clean bill.
  • Write prose as JSON, never as code: the 16 August draft lost every apostrophe because its items were authored inside single-quoted JavaScript strings. Write the batch with a file-writing tool and append it with scripts/work-log.mjs append.
  • No digits in curated prose (titles, summaries, headline, next-up, frontier): the numeric fact fence aborts the build on them.
  • Run discovery from a sibling checkout (C:\Users\Bryce\Projects\brycewatson.com-*), never from a worktree nested under .claude\worktrees\: the digests look for the other repositories one folder up and silently come back empty.

Scheduling (every Sunday night) — Claude scheduled task

The unattended weekly run is a Claude scheduled task (mcp__scheduled-tasks), NOT a Windows Task Scheduler job. It runs as a local Claude agent with full filesystem access and catches up on next app launch if the app/machine was closed at the scheduled time — fixing the old schtasks job's silent-drop failure (it had StartWhenAvailable=False / WakeToRun=False / DisallowStartIfOnBatteries=True and no catch-up).

  • The task: taskId: weekly-work-log, cronExpression: "0 22 * * 0" (Sunday 22:00 local). Its prompt is authored in scheduled-task-prompt.md (this skill dir) — keep that file in sync with the live task. Its Monday preview prompt is authored in preview-scheduled-task-prompt.md and accepts only a verified work-log/weekly-* PR that changes both the authored source and generated report.
  • Create / update it (the runtime swap; done by an operator session after the repo seams are on main):
    mcp__scheduled-tasks__create_scheduled_task({
      taskId: "weekly-work-log",
      cronExpression: "0 22 * * 0",
      description: "Sunday-night Weekly Work Log: deterministic build + verify, fail-open advisory, opens one review PR (never deploys)",
      prompt: <contents of scheduled-task-prompt.md>
    })
    Confirm with mcp__scheduled-tasks__list_scheduled_tasks.
  • Retire the legacy Windows task at the same swap: node uninstall.cjs (or schtasks /Delete /TN ClaudeWeeklyWorkLog /F). install.cjs / weekly-run.cmd are the legacy schtasks launchers, retired at the swap; do not run install.cjs going forward.
  • Failure visibility: every run starts by writing last-run.json under the live scheduled-task directory and must write one terminal state. Fetch/worktree setup and a non-data PR-open failure retry exactly once; data verification never retries into a PR. One open weekly PR left over from an earlier week is rolled forward into this week's PR; several are a failure (STALE_WEEKLY_PR), and a roll-forward that conflicts with main is a failure (ROLLFORWARD_CONFLICT), never a success.
  • The Monday preview (weekly-work-log-preview, prompt in preview-scheduled-task-prompt.md) reads that state. A failure, a stale PR, a missed run, or a held merge is raised to Bryce through the ask skill. A merged week becomes a "merged, here's what went live" note with the candidates and one ask for his labeled replies. It launches a localhost preview only for an open PR.
  • Deploying a prompt change: edit the prompt in claude-global-skills, run python scripts/sync.py --deploy, then paste the text below the --- rule into update_scheduled_task for weekly-work-log or weekly-work-log-preview.
  • Verification debt: the run-on-next-launch catch-up is documented, not yet behaviorally proven here. Worth a one-time controlled catch-up test (close the app over a due time, relaunch, confirm fire) before fully trusting it — the demandforge-verify-cron-first-firing task is precedent.
  • It activates once the page is shipped (the data files are committed). Requires: gh authenticated and git push credentials. The shared repo may be on any branch and may contain the user's work because the scheduled run operates from origin/main in its own worktree.

Safety invariants

  • Never push to main; never deploy; never gh pr merge directly. The unattended run opens exactly one PR, and only work-log-merge-gate.mjs may merge it.
  • Never mutate the user's shared checkout. All unattended writes happen in the dedicated worktree; every setup or terminal failure is persisted before the task exits.
  • Abort (no PR) if the generator fails verification — never open a PR with unverified data.
  • The LinkedIn candidate and post card never publish, post, or draft a post by themselves. A draft answer from Bryce becomes one issue on BryceEWatson/brycewatson.com, labelled needs-bryce and linking the approved card, for a session that drafts the post through the site's normal gates; a re-run finds that issue by the card's changeId instead of filing a second one. The approved answer stays recorded in the candidates file, and every run opens an issue for any approved card that still has none, so a request whose issue could not be opened (a gh failure) is retried the next week rather than lost.
  • Advisory is fail-open, but never silently: any LLM/network/gh failure → the "advisory unavailable" line PLUS a line naming which failure it was (not requested / did not run / empty / withheld by the privacy screen). The PR still opens with deterministic data + the human checklist intact. Advisory may only DOWNGRADE/FLAG — never clear-for-publish, never suppress the human leak/voice checklist. A reviewer must always be able to tell a failed advisory from a clean one; an absent advisory is not a clean bill of health.
  • The cron NEVER fires --week backfill; the harvester's raw nouns NEVER enter the PR body (count only).
  • Reuse the review-loop --mode claim engine/rubric verbatim for the badge-vs-prose checks — read its agents/claim-*.md lenses at runtime; do not fork them.
  • Curate every session in an allowlisted repo, redacted — not dropped, not stubbed. A session outside them is excluded entirely (see Privacy). Private/sensitive sessions are summarized through the privacy filter (display roles never git-read; client→"Akaya", finances→"Personal"; generalized to the kind of work; accuracy is the hard floor even for private). Akaya rows reach brycewatson.com only once vetted: the scheduled run gives each new one a keep, reword or drop verdict in the site's src/data/akaya-work-log.json (rules in its docs/client-work-log.md), and an unvetted row stays hidden. A session you cannot distil confidently gets the most generic HONEST entry, never a fabricated one.
  • The drafted prose is gated before it builds: work-log-validate-source.mjs fails loud on a dash/leak/bad-badge/git-leak/apostrophe-free batch; the iron-clad redactor + the dist-PII scan are the deterministic backstop; the merge gate publishes only a green, unflagged week under the 2026-09-10 ruling, and everything else is Bryce's call.
  • The unattended run writes ONLY work-log.source.* (its drafted items), data/weekly-candidates/*, the findings ledger and the mine block of honestweek.config.json, and gitignored sidecars; it NEVER hand-writes work-log.json / reports/* / goals.json (the build does, deterministically), never main, never deploy.
  • The drafts digest and harvester sidecar are gitignored and redacted; never commit them.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.