Skip to content

docs: contribution policy — CONTRIBUTING, PR template, changelog CI guards - #100

Open
Anarchid wants to merge 3 commits into
mainfrom
docs/contribution-policy
Open

docs: contribution policy — CONTRIBUTING, PR template, changelog CI guards#100
Anarchid wants to merge 3 commits into
mainfrom
docs/contribution-policy

Conversation

@Anarchid

@Anarchid Anarchid commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Problem

agent-framework has no CONTRIBUTING.md, no PR template, and no changelog —
and no mechanism that would notice one was missing. @animalabs/agent-framework
is at 0.9.0 on npm, and the newest GitHub release in this repo is v0.3.0 from
April
. Everything since is discoverable only by reading git log.

Three releases have shipped in the last two weeks, two of them minors carrying
real breaking changes, and none of them announced anywhere a consumer would
look:

  • 0.8.0 renamed McplServerConfig.tokenProvideraccessProvider.
  • 0.9.0 removed a public method from an exported class, changed a fleet
    default (tool results now spill at 5,000 chars), enforced MCPL 0.5
    deny-by-default before the policy handshake — which darkens un-migrated
    MCPL servers
    — and raised the chronicle floor to ^0.3.0, whose store
    format 0.2.x cannot reopen. That last one wants cold backups before
    upgrading a residence, and the only place it was ever written down is a
    release commit body.

A PR-side check alone can't carry the rule: across the ecosystem 52–85% of
commits on main land by direct push, and version bumps are release-time
maintainer actions by construction. The load-bearing layer is the tag-time
guard. This is the third rollout of the same policy, after connectome-host #49
and context-manager #51.

Changes

Ported from those two, adapted to this repo (build-before-test caveat,
stale-dist/ warning in the template):

  • CONTRIBUTING.md — codifies existing practice rather than aspiration:
    merge commits only, comment-based review with run-the-branch transcripts,
    evidence-over-assertion test reporting, and AI attribution as the norm.
    Explicitly binds direct pushes and PRs, human and AI authors, identically.
  • .github/PULL_REQUEST_TEMPLATE.md — Problem / Changes / Tests / Not
    verified, plus a changelog checkbox.
  • .github/workflows/changelog.yml — soft PR check: touching src/
    without CHANGELOG.md fails, with a no-changelog label opt-out (label
    created).
  • publish.yml — the publish job is now gated on the ref rather than the
    event, so a manual dispatch against a branch can no longer publish
    whatever version package.json happens to carry; a tag with no matching
    ## X.Y.Z section fails the release; and a new github-release job mirrors
    the tag's section into GitHub release notes, independent of npm publish so
    notes exist even when publish fails.
  • scripts/release-changelog.mjs + a version hook in package.json
    npm version <level> cuts Unreleased into ## X.Y.Z — YYYY-MM-DD
    pre-tag, so the tag and tarball carry it. Refuses an empty Unreleased, a
    duplicate section, and more than one Unreleased heading (a second one
    silently strands entries).
  • CHANGELOG.md, watermarked at 0.7.3, with 0.7.4, 0.8.0 and 0.9.0
    written up in full.

That last point is deliberate. The earliest policy PRs held that the
enforcement PR should itself conform; this branch was cut 2026-07-27 and three
releases have shipped since. Landing an empty changelog would mean adding a
file already three releases behind main — the exact failure this PR exists
to prevent, committed by the PR that prevents it.

Entries are reconstructed from v0.7.3..v0.9.0 and grouped by the release
that carried them. Notes on the judgment calls:

  • Breaking entries are audience-scoped, as the policy asks: consumers
    (the sendAfterInference removal, the spill default), MCPL servers (0.5
    deny-by-default, §7 removal), and dependency floors separately — the three
    audiences act on different things, and only the third needs the backup
    warning.
  • 0.9.0's release commit already itemized its breaking surface and floors;
    that is carried over rather than re-derived.
  • Review-response commits are folded into the entry for the feature they
    correct
    , not listed separately — the changelog describes released
    behavior, not the path to it.
  • Test-only and refactor commits are omitted, per the policy's own rule about
    what needs an entry.

The package.json conflict from the rebase was resolved in favour of main's
--test-force-exit test script, with only the version hook added.

Tests

npm test: 560 tests, 560 pass, 0 fail, 0 skipped (70 suites). npm run build clean; tsc --noEmit: 0 errors. Green on this branch, ahead of the
539/538/1-skipped baseline the 0.9.0 release commit reported.

No src/ changes — this is docs, CI config, and one npm lifecycle hook — so
the suite is a regression check, not a demonstration. What was exercised
directly is the tooling this branch adds, against the real CHANGELOG.md:

  • Tag guard simulated for all three sections: 0.7.4, 0.8.0, 0.9.0
    all match.
  • Release-notes extraction, same awk as the workflow: 45 / 79 / 162 lines
    respectively, each starting at its first ### heading and stopping at the
    next ## — no bleed across section boundaries.
  • scripts/release-changelog.mjs against a copy, package.json at 0.9.1:
    empty Unreleased → refused, exit 1; one entry added → cut Unreleased into '## 0.9.1 — 2026-08-07', exit 0, fresh empty Unreleased left above it;
    re-run → refused as duplicate, exit 1.
  • Unreleased heading count: exactly 1.
  • All three workflow files parse as YAML.

One reproduction note that cost time here and may cost a reviewer the same:
the workspace-symlinked membrane and context-manager checkouts had stale
dist/ (membrane's dated June, against 0.5.78 sources), which fails AF's
build on the retrying event type with 5 TS2678/TS2339 errors in
framework.ts. Rebuild those two first — it is a local-environment artifact,
not a branch or main defect, and CI installs from the registry so it cannot
hit this.

Not verified

  • No agent was run. Nothing in this branch touches a runtime path, so no
    live inference, MCPL connection or store was exercised.
  • The changelog.yml check has not run yet — this PR touches no src/ files,
    so it will pass trivially rather than exercising its failure path. The
    equivalent workflow was verified live on connectome-host test: add channel_publish fence coverage (home-default + foreign-reject) #49.
  • The tag guard and github-release job cannot run until the next release and
    stay unexercised on real infrastructure until then, as on the previous two
    rollouts. The next npm version <level> && git push --follow-tags will be
    this repo's first GitHub release since v0.3.0.
  • The 0.7.4–0.9.0 entries are reconstructed from commit messages and the
    measurements quoted in them, not re-measured. Incident details (token
    counts, outage durations, crash-cycle counts, dates) are as their authors
    reported them. Where a commit body and a release body disagreed on framing,
    I took the release body.
  • I did not audit whether any consumer actually calls
    McplServerConnection.sendAfterInference; the changelog repeats chore(mcpl): drop the dead context/afterInference send surface (issue #39) #86's own
    14-tree grep rather than re-running it.

  • CHANGELOG.md updated under ## Unreleased — or this change is
    internal-only / test-only / docs-only (apply the no-changelog label).

Docs-only by the letter of the check, but the file ships with three releases
written up rather than empty, per the self-conformance point above.

🤖 Generated with Claude Code

Anarchid and others added 3 commits August 7, 2026 14:15
Ports the policy set deployed to connectome-host (PR #49) to this repo,
codifying norms already practiced here rather than introducing new ones:
merge-commit-only history, comment-based evidence review, declared AI
authorship, companion-PR merge-order declarations.

Adds the changelog discipline this repo has never had — 25 releases, no
changelog — with enforcement layered cheapest-first: an entry-with-the-change
rule binding direct pushes as well as PRs, a soft PR check (src/ touched =>
CHANGELOG.md touched, `no-changelog` label escape), and a tag-time publish
guard that refuses to release a tag with no matching section. A
`github-release` job mirrors that section into the GitHub release notes,
deliberately independent of npm publish so github-clone consumers still get
notes when publish fails.

`npm version` now cuts the Unreleased section via scripts/release-changelog.mjs.
Two hardenings over the connectome-host original, whose CHANGELOG.md
accumulated six `## Unreleased` headings with one stranding ~60 lines of
entries that reached no release:

- refuses to run when more than one `## Unreleased` heading exists, since
  only the first is ever cut and later ones are silently never released;
- splices by match index instead of `String.replace(substring)`, which hits
  the first *substring* occurrence — an inline `## Unreleased` mention in
  prose comes first and the version heading lands inside it.

Verified: release script refuses the empty Unreleased (exit 1); full ritual
exercised on a copy — entry -> cut -> tag guard pass/refuse -> notes
extraction; all three workflows parse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This branch was cut 2026-07-27; three releases have shipped since,
including two minors. The policy it introduces binds direct pushes as
much as PRs — and version bumps are direct-pushed release-time actions by
construction — so landing the file already three releases behind main
would undercut the point of adding it.

Entries reconstructed from v0.7.3..v0.9.0, grouped by the release that
carried them. 0.9.0's release commit already itemized its own breaking
surface and dependency floors; those are carried over rather than
re-derived, including the chronicle 0.3.0 cold-backup deploy note, which
belongs in front of anyone upgrading a residence.

Breaking entries are audience-scoped per the policy: consumers
(sendAfterInference removal, the 5000-char spill default), MCPL servers
(0.5 deny-by-default before the policy handshake, §7 removal — un-migrated
servers go dark), and dependency floors separately, since the audiences
act on different things.

Test-only and refactor commits are omitted per the policy's own rule.
Review-response commits are folded into the entry for the feature they
correct rather than listed separately.

Verified: tag guard matches all three sections; release-notes awk extracts
45/79/162 lines with no cross-section bleed; release-changelog.mjs refuses
the empty Unreleased, cuts 0.9.1 cleanly, then refuses the duplicate.
Build clean, tsc --noEmit clean, npm test 560/560 pass 0 fail.

Note for anyone reproducing that suite locally: the workspace-symlinked
membrane and context-manager checkouts had stale dist/ (membrane's from
June, against 0.5.78 sources), which fails AF's build on the 'retrying'
event type. Rebuild those two first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…edentials

Greptile flagged the new github-release job (contents: write) for running
actions/checkout at a mutable major-version ref: retargeting that tag would
change the code running with permission to create and edit releases.

Valid, and it undersells the exposure — that job is the LEAST privileged of
the write-capable ones. Fixed across every workflow rather than just the
flagged line, because a half-pinned repo invites the same finding next time:

- 8 actions pinned (checkout, setup-node) across changelog.yml, ci.yml and
  publish.yml.

Two things the review did not name:

- The npm publish job holds `id-token: write` for OIDC trusted publishing.
  A swapped action there can reach a live publish credential, which is a
  worse outcome than editing release notes.
- checkout defaults to persist-credentials: true, writing the job token into
  .git/config where every later step in the job can read it. Nothing here
  pushes over git — publish uses OIDC, the release job uses gh with GH_TOKEN
  — so all 4 checkouts now set it false.

Version tags are kept as trailing comments so the pins stay readable and
Dependabot can still bump them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant