weekly-work-log — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited weekly-work-log (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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 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).
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-saferunning / 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 openweekly 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.
.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).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.
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.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>".gh failure → a single "advisory unavailable" line; never blocks the PR. Advisory may only DOWNGRADE/FLAG.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.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.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.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.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.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.pnpm dev, open http://localhost:4321/weekly-report (archive index at /log, goals at /goals).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.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".)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".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).[email protected] / [email protected]) aborts the build. Item dates come from the commit, not the prose.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).archive → link to /weekly-report/<project> (6-month commit history). Dark console aesthetic; the shared site header uses headerVariant="dark-canvas".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.scripts/work-log.mjs append.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.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).
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.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.
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.last-run.json under the livescheduled-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.
weekly-work-log-preview, prompt inpreview-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.
claude-global-skills, runpython scripts/sync.py --deploy, then paste the text below the --- rule into update_scheduled_task for weekly-work-log or weekly-work-log-preview.
demandforge-verify-cron-first-firing task is precedent.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.
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.worktree; every setup or terminal failure is persisted before the task exits.
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.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.--week backfill; the harvester's raw nouns NEVER enter the PR body (count only).--mode claim engine/rubric verbatim for the badge-vs-prose checks — read its agents/claim-*.md lenses at runtime; do not fork them.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.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.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.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.