Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions .github/docs/WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,12 @@ Extracts the PR's size label and maps to emoji for the workflow summary.
| Step | Name | What it does |
|------|------|-------------|
| 0-0a | Cache | pnpm store, keyed on `pnpm-lock.yaml` |
| 2 | Setup GitHub Config | Git config |
| 3 | Release notes | Runs `scripts/get_github_release_log.sh` |
| 4 | Prepare release | `corepack pnpm install --frozen-lockfile && corepack pnpm run release` + push tags to main |
| 5 | Get release tag | Extract latest tag |
| 6 | Publish release | `ncipollo/release-action@v1` |
| 1 | Setup GitHub Config | Git config |
| 1a | **Dogfood — refresh vendored agent skills** | Resolves the latest tag of `DailybotHQ/deepworkplan-skill` and `DailybotHQ/agent-skill` via `gh release view`, and if either is newer than the vendored copy under `.agents/skills/`, runs `npx --yes skills add <repo>@<tag> --skill <name> --force -y`, asserts the version invariant, and commits `chore: dogfood vendored skills to (…)`. **Runs before Step 2** so the dogfood commit lands in the release notes. See "Dogfood step" below for details. |
| 2 | Release notes | Runs `scripts/get_github_release_log.sh` — includes the dogfood commit from Step 1a if it exists |
| 3 | Prepare release | `corepack pnpm install --frozen-lockfile && corepack pnpm run release` + push tags to main (pushes the dogfood commit alongside the version-bump commit + tag in one atomic push) |
| 4 | Get release tag | Extract latest tag |
| 5 | Publish release | `ncipollo/release-action@v1` |

**Helper script:** `scripts/get_github_release_log.sh`
- Reads `git log --pretty=oneline`
Expand All @@ -156,6 +157,30 @@ Extracts the PR's size label and maps to emoji for the workflow summary.
- Prefixes each entry with `🚩`
- Creates `git_logs_output.txt` as release body

#### Dogfood step (Step 1a)

**Purpose.** Every website release should ship with a current snapshot of the two agent skills it documents. Rather than a scheduled/autonomous refresh, the update happens **exactly when the maintainer decides to cut a release** — no background cron, no drift between "when a skill dropped" and "when someone releases the site".

**What runs.**

1. `gh release view --repo <owner/repo> --json tagName -q .tagName` for each of `DailybotHQ/deepworkplan-skill` and `DailybotHQ/agent-skill` to resolve the latest tag.
2. Reads the currently vendored versions from `.agents/skills/{deepworkplan,dailybot}/SKILL.md` frontmatter.
3. Per-skill "moved" flag → only installs the skills that actually changed. Commits and PR titles list only the moved skills (no misleading "dogfood to deepworkplan vX, dailybot vY" when only one moved).
4. `npx --yes skills add <repo>@<tag> --skill <name> --force -y` — exact same command any downstream consumer would run. Serves as a live smoke test that the upstream release is installable.
5. Version invariant: the installed `SKILL.md` frontmatter `version:` MUST equal the requested tag. Fails the release if not — refuses to publish a website tag that misrepresents its vendored skills.
6. If any files changed, commits `chore: dogfood vendored skills to <list>` locally. Step 3's `git push --follow-tags` sends this commit alongside the version-bump commit and the tag in a single atomic push.

**Failure semantics.**

| Failure | Behavior |
|---------|----------|
| `gh release view` fails for one upstream repo (rate limit, transient outage) | Warns; skips that skill. The other skill (and the release) proceed. |
| `npx skills add` fails | **Fails the release** — real upstream breakage, must be surfaced. |
| Version invariant mismatch after install | **Fails the release** — refuses to commit a misrepresented dogfood snapshot. |
| Both skills already at latest | Clean no-op — no dogfood commit, release proceeds. |

**Manual refresh outside of a release.** Not supported by design. If a maintainer needs to bump a vendored skill without cutting a full release, do it locally (`npx skills add <repo>@<tag>`) on a PR branch and merge it as a normal `chore:` change — that will trigger a release naturally.

### Job 3: `cleanup_caches` (depends on: Job 2)

See [DEPLOYMENT.md](./DEPLOYMENT.md) for Cloudflare Pages setup.
Expand Down
142 changes: 142 additions & 0 deletions .github/workflows/release_and_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,148 @@ jobs:
git config user.name "deepworkplan-bot"
git config user.email "bot@deepworkplan.com"
git config advice.skippedCherryPicks false
- name: Step 1a - 🐕 Dogfood — refresh vendored agent skills to latest upstream
# Before every website release, bring the two vendored skills under
# `.agents/skills/` up to their latest published upstream tags —
# so `vX.Y.Z` of this site always ships with a current snapshot of
# `deepworkplan` and `dailybot`. Mirrors the "Dogfood" step in
# DailybotHQ/deepworkplan-skill's own `auto-release.yml`, adapted
# to install third-party upstream tags instead of this repo's own.
#
# This step runs BEFORE Step 2 on purpose: `get_github_release_log.sh`
# in Step 2 collects commit subjects from the previous release
# marker up to HEAD; committing the refresh here ensures the
# dogfood commit lands in the GitHub Release notes alongside the
# PR that triggered this run.
#
# Failure semantics:
# * Upstream API blip while resolving a tag → warn + skip THAT
# skill (a temporary hiccup with `gh release view` should NOT
# block a website release that was going to happen anyway).
# * `npx skills add` failure OR version-invariant mismatch →
# FAIL the release (we should not publish a website tag that
# misrepresents which skill versions it vendors).
# * Both skills already at latest → clean no-op, release proceeds
# normally without a dogfood commit.
env:
GH_TOKEN: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
run: |
set -euo pipefail

# Resolve one upstream skill to a concrete tag. Prints "" on
# failure so callers can skip cleanly.
#
# CRITICAL: all logging goes to stderr (>&2). The caller uses
# `X=$(resolve ...)` — any `::notice::`/`::warning::` on stdout
# would be captured into the variable and poison the equality
# checks below. GitHub Actions still renders workflow commands
# emitted to stderr.
resolve() {
local repo="$1" name="$2"
local tag
tag=$(gh release view --repo "$repo" --json tagName -q .tagName 2>/dev/null || true)
if [ -z "$tag" ]; then
echo "::warning::${name}: could not resolve latest release for ${repo} — skipping this skill this release" >&2
printf '\n'
return 0
fi
echo "::notice::${name}: latest published release is ${tag}" >&2
printf '%s\n' "$tag"
}

read_version() {
sed -nE 's/^version:[[:space:]]*"([^"]+)".*/\1/p' "$1" | head -n1
}

install_and_verify() {
local repo="$1" skill_name="$2" skill_dir="$3" tag="$4"
local new_version="${tag#v}"
# Two `-y` / `--yes` flags below — both required in a non-TTY
# environment: `npx --yes` skips the "install the package?"
# prompt, `skills add ... -y` skips the interactive agent picker.
echo "::group::${skill_name}: npx skills add ${repo}@${tag}"
npx --yes skills add "${repo}@${tag}" --skill "$skill_name" --force -y
echo "::endgroup::"
# Invariant: what got installed MUST equal what we asked for.
local vendored
vendored=$(read_version "${skill_dir}/SKILL.md")
if [ "$vendored" != "$new_version" ]; then
echo "::error::${skill_name}: installed version '${vendored}' does not match requested tag '${tag}' (expected '${new_version}')"
exit 1
fi
}

DWP_TAG=$(resolve DailybotHQ/deepworkplan-skill deepworkplan)
DB_TAG=$(resolve DailybotHQ/agent-skill dailybot)

DWP_CUR=$(read_version .agents/skills/deepworkplan/SKILL.md)
DB_CUR=$(read_version .agents/skills/dailybot/SKILL.md)

# Only install skills that would actually change — no wasted work
# and no misleading dogfood commit when one skill is at latest
# and the other is not.
DWP_MOVED=false
DB_MOVED=false
[ -n "$DWP_TAG" ] && [ "$DWP_CUR" != "${DWP_TAG#v}" ] && DWP_MOVED=true
[ -n "$DB_TAG" ] && [ "$DB_CUR" != "${DB_TAG#v}" ] && DB_MOVED=true

[ "$DWP_MOVED" = "true" ] && install_and_verify DailybotHQ/deepworkplan-skill deepworkplan .agents/skills/deepworkplan "$DWP_TAG"
[ "$DB_MOVED" = "true" ] && install_and_verify DailybotHQ/agent-skill dailybot .agents/skills/dailybot "$DB_TAG"

# Stage only the paths the installer is allowed to touch.
git add \
.agents/skills/deepworkplan \
.agents/skills/dailybot \
skills-lock.json 2>/dev/null || true

if git diff --cached --quiet; then
echo "Vendored skills already at latest — no dogfood commit needed."
{
echo "## Vendored skills"
echo ""
echo "| Skill | Vendored | Latest upstream | Moved? |"
echo "| --- | --- | --- | --- |"
echo "| deepworkplan | v${DWP_CUR} | ${DWP_TAG:-_(unresolved)_} | ${DWP_MOVED} |"
echo "| dailybot | v${DB_CUR} | ${DB_TAG:-_(unresolved)_} | ${DB_MOVED} |"
echo ""
echo "**No dogfood commit** — vendored copies already match the latest upstream releases."
} >> "$GITHUB_STEP_SUMMARY"
exit 0
fi

# Build a commit subject that lists ONLY the skills that moved.
# `${array[*]}` only uses the first character of IFS when joining,
# so build the joined string by hand to preserve "a, b" (with the
# space).
parts=()
[ "$DWP_MOVED" = "true" ] && parts+=("deepworkplan ${DWP_TAG}")
[ "$DB_MOVED" = "true" ] && parts+=("dailybot ${DB_TAG}")
MSG=""
for p in "${parts[@]}"; do
if [ -z "$MSG" ]; then MSG="$p"; else MSG="${MSG}, ${p}"; fi
done

# Commit locally. Step 3 ('Prepare release') runs right after
# and does `pnpm run release && git push --follow-tags origin
# main`, which pushes THIS commit alongside the version-bump
# commit + tag in a single atomic push. The commit shows up in
# the release body because Step 2 (get_github_release_log.sh)
# runs after this step and walks the commit range from the
# previous release marker up to HEAD.
git commit -m "chore: dogfood vendored skills to ${MSG}"

{
echo "## Vendored skills"
echo ""
echo "| Skill | Vendored | Latest upstream | Moved? |"
echo "| --- | --- | --- | --- |"
echo "| deepworkplan | v${DWP_CUR} | ${DWP_TAG:-_(unresolved)_} | ${DWP_MOVED} |"
echo "| dailybot | v${DB_CUR} | ${DB_TAG:-_(unresolved)_} | ${DB_MOVED} |"
echo ""
echo "**Dogfood commit:** \`chore: dogfood vendored skills to ${MSG}\`"
echo ""
echo "This commit is pushed alongside the version-bump commit in Step 3."
} >> "$GITHUB_STEP_SUMMARY"
- name: Step 2 - 📄 Set GitHub release content "BODY" env var
run: |
bash .github/scripts/get_github_release_log.sh
Expand Down
21 changes: 21 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,27 @@ This repo has the DWP **Dailybot addon** wired: the `dailybot` skill is installe

**Deterministic hook enforcement (Claude Code):** `.agents/settings.json` wires the Dailybot lifecycle hooks (`dailybot hook session-start | activity | stop`, CLI >= 1.12.0) so the harness itself detects unreported work and reminds the agent at end of turn — no reliance on the model remembering. When a reminder fires: send a report if a meaningful unit of work is done, or run `dailybot hook dismiss` if not — never ignore it silently, and never let reporting block work. The hooks are local-only, always exit 0, and respect `.dailybot/disabled`.

### Vendored agent skills — refreshed on every website release

`.agents/skills/deepworkplan/` and `.agents/skills/dailybot/` are **vendored copies** of the upstream skill repos (`DailybotHQ/deepworkplan-skill` and `DailybotHQ/agent-skill`), tracked in git and pinned via `skills-lock.json`. They are refreshed **automatically as part of every website release**, so `vX.Y.Z` of the site always ships with a current snapshot of both skills.

**How it works.** [`release_and_publish.yml`](.github/workflows/release_and_publish.yml) (which fires on every merge to `main`) has a dogfood step (Step 1a) that runs **before** the version bump:

1. Resolves the latest tag of each upstream skill via `gh release view --repo <owner/repo>`.
2. Compares against the vendored `SKILL.md` `version:` field. Only installs skills that actually moved.
3. Runs `npx --yes skills add <repo>@<tag> --skill <name> --force -y` — the exact command any downstream consumer would run, so this doubles as a live smoke test.
4. Asserts the invariant: installed `SKILL.md` version equals the requested tag. Refuses to proceed with the release if not.
5. If any files changed, commits `chore: dogfood vendored skills to (…)` locally. Step 3's `git push --follow-tags` sends this commit alongside the version-bump commit and the tag in a single atomic push, and the dogfood commit appears in the auto-generated GitHub Release notes.

**Semantics.** Skill refresh is **release-driven**, not autonomous — no scheduled/cron refresh runs in the background. The vendored copies advance only when a maintainer merges a PR to `main`, which is the same moment `release_and_publish.yml` cuts a new website release. The intent is that the maintainer controls exactly when the site adopts a new skill version.

**Failure semantics.**
- `npx skills add` failure OR version-invariant mismatch → **fails the release** (a broken upstream tag must never quietly ship inside a website version).
- Transient `gh release view` blip (rate limit, temporary outage) → skips only that skill for this release; the release itself proceeds.
- Both skills already at latest → clean no-op, no dogfood commit, release proceeds normally.

**Do not edit files under `.agents/skills/deepworkplan/` or `.agents/skills/dailybot/` by hand** — the next release will overwrite hand edits. Contribute upstream, then merge any PR to trigger a website release that picks up the new upstream tag.

## Quick Commands

```bash
Expand Down
Loading