Skip to content

No automated versioning or changelog, and the commit convention is unenforced #2

Description

@ayushnaikccino

@thegeorgepu — now that the repository is nearly ready to open source, the one substantial gap left is release bookkeeping. Two related problems, both cheap to fix, and one of them blocks the other.

To be clear about scope up front: the release pipeline is not the problem. vinci-release.yml is stronger than anything else we run — build-then-verify with the tested payload carried forward rather than rebuilt, OIDC-assumed role, write-once upload, signed manifest published last as the single activation point, client verification against a pinned key. None of that needs changing. What is missing is the bookkeeping around it: deriving the version, and generating the changelog.


1. The version is maintained by hand in three places

A release today requires editing three files that must agree exactly, and vinci-release.yml hard-fails if they do not:

Source Current value
the vinci-v* tag supplied by hand at release time
vinci/identity.json.version 0.0.49
vinci/extensions/vinci-header.tsVINCI_VERSION 0.0.49
# .github/workflows/vinci-release.yml
test "$TAG_VERSION" = "$IDENTITY_VERSION" || { echo "identity.json disagrees with the tag"; exit 1; }
test "$TAG_VERSION" = "$HEADER_VERSION"   || { echo "vinci-header.ts disagrees with the tag"; exit 1; }

The guard is good — it fails closed. But it is catching a class of mistake that should not be possible, and it only runs once a tag is already pushed.

There is also a fourth version number that nothing checks: package.json says 0.0.3 while the product is at 0.0.49. Whatever it was meant to track, it stopped.

2. Release notes exist for 2 of ~49 versions

vinci/release-notes/ contains 0.0.36.md and 0.0.49.md. Everything else shipped with no notes, and that is by design:

If there is no file for the version, the step is skipped and the release proceeds normally — writing notes is optional, and forgetting to never blocks a release.
vinci/release-notes/README.md

Sensible while private. Once vinci-code-releases is a public tracker, a release appearing with an empty body is what users see. There is also no root CHANGELOG.md.

3. Nothing to automate from — commit messages carry no information

This is the blocker, and it is why I would not just drop a tool in and call it done.

Every Vinci commit on main has the identical subject:

$ git log 244f1dea..HEAD --format='%s' | sort | uniq -c
      4   Vinci Code — the Vinci distribution layer

Four commits, one message, repeated. No tool can derive patch vs minor from that, and a generated changelog would have four identical lines. Upstream, by contrast, is 19 of its last 20 commits in Conventional Commits form — the convention works fine in the parts of the tree we did not write.

So the exporter has to emit real commits before any release automation is worth adding. Splitting each export into per-change commits with real subjects is the prerequisite; everything below depends on it.

4. The commit convention is documented where contributors will not see it

A format does exist — in exactly one place:

AGENTS.md:56
- Message format: `{feat,fix,docs}[(ai,tui,agent,coding-agent)]: <commit message> ...`

AGENTS.md is loaded into the agent's context at runtime (resource-loader.ts lists it first among context-file candidates), so coding agents see it. Humans do not: CONTRIBUTING.md mentions the commit format zero times. Nothing enforces it either — husky has a pre-commit hook but no commit-msg hook, and there is no commitlint config.

Two problems with the convention itself:

  • The scope list has no vinci. It reads (ai,tui,agent,coding-agent) — upstream's packages. The Vinci layer, where essentially all of our work happens, has no scope of its own.
  • Only feat, fix, docs are allowed. There is no chore, refactor, test, ci, build or perf, so anything that is not a feature or a bug gets mislabelled. A CI change becomes fix: and silently implies a patch release to any tool reading these.

Recommendation

Release automation: release-it

release-it fits this repository better than release-please or semantic-release, for a specific reason: it stops at the tag.

Our release is triggered by pushing vinci-v*, and everything valuable — signing, OIDC, write-once upload, manifest activation — happens after that. release-please wants to own tagging through its own release PR and would fight that design. semantic-release wants to own publishing. release-it bumps, changelogs, commits, tags and pushes, then gets out of the way, and vinci-release.yml takes over exactly as it does today. No change to the signing pipeline.

It also solves the three-places problem directly, via hooks that update identity.json and vinci-header.ts from the single version it computes — turning the CI guard from something that catches human error into something that should never fire.

// .release-it.json
{
  "git": {
    "tagName": "vinci-v${version}",
    "commitMessage": "chore(release): vinci-v${version}",
    "requireBranch": "main",
    "requireCleanWorkingDir": true
  },
  "github": { "release": false },   // vinci-release.yml owns publishing
  "npm": { "publish": false },      // we ship a binary, not npm packages
  "hooks": {
    "after:bump": [
      "node vinci/scripts/set-version.mjs ${version}",  // identity.json + vinci-header.ts
      "git add vinci/identity.json vinci/extensions/vinci-header.ts"
    ]
  },
  "plugins": {
    "@release-it/conventional-changelog": {
      "preset": "conventionalcommits",
      "infile": "CHANGELOG.md"
    }
  }
}

npm i -D release-it @release-it/conventional-changelog. The conventional-changelog plugin derives the bump from commit types and writes the changelog, which also fixes problem 2 — and vinci/release-notes/<version>.md can stay for hand-written user-facing notes, with the generated changelog as the complete record underneath.

Do not adopt this before the exporter emits real commits (problem 3), or the bump will be meaningless.

Commit conventions: commitlint on a commit-msg hook

husky is already installed; this is one more hook.

npm i -D @commitlint/cli @commitlint/config-conventional
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
// commitlint.config.js
export default {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "type-enum": [2, "always",
      ["feat", "fix", "docs", "refactor", "test", "chore", "ci", "build", "perf", "revert"]],
    "scope-enum": [2, "always",
      ["vinci", "ai", "tui", "agent", "coding-agent", "orchestrator", "updater", "release", "deps"]],
    "subject-case": [2, "never", ["upper-case", "pascal-case", "start-case"]],
    "header-max-length": [2, "always", 100],
  },
};

Three things this changes beyond enforcement:

  1. Adds a vinci scope, so our own work can be labelled accurately.
  2. Widens the type list, so a CI change is ci: and does not imply a release.
  3. Makes the rule machine-readable, which is what release-it needs anyway — the same config powers both.

Then document it where humans look: a short section in CONTRIBUTING.md (currently silent on it), and a line in the PR template. AGENTS.md:56 should be updated to match rather than being the only source of truth.

Suggested order

  1. Exporter emits per-change commits in Conventional Commits form — prerequisite
  2. commitlint + commit-msg hook; update AGENTS.md, CONTRIBUTING.md, PR template
  3. vinci/scripts/set-version.mjs writing identity.json and vinci-header.ts from one argument
  4. release-it + conventional-changelog; generate the first CHANGELOG.md
  5. Decide what package.json's 0.0.3 is for — track it or drop it

Steps 2 and 3 are useful on their own and do not depend on step 1. Only step 4 does.

Happy to take any of this if it is useful — flagging rather than assuming, since the exporter change is yours.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions