Skip to content

fix(docs): update vulnerable astro, starlight, and sharp dependencies - #51

Merged
martient merged 1 commit into
developfrom
fix/docs-vulnerable-deps
Sep 27, 2026
Merged

martient merged 1 commit into
developfrom
fix/docs-vulnerable-deps

Conversation

@martient

Copy link
Copy Markdown
Owner

Summary

  • Bumps astro (5.13.3 → ^7.3.5), @astrojs/starlight (0.35.2 → ^0.42.4), and sharp (0.34.3 → ^0.35.5) in the docs site, resolving all 89 Dependabot alerts (2 critical, 31 high, 42 moderate, 14 low) — all were transitive deps (vite, js-yaml, postcss, nanoid, h3, picomatch, devalue, and others) of these outdated pins.
  • Updates astro.config.mjs for a Starlight 0.39+ breaking change: sidebar groups can no longer combine label with a top-level autogenerate; each autogenerate now lives inside an items array.
  • Nothing in Cargo.toml/Cargo.lock (the CLI itself) was affected — all alerts were scoped to docs/.

Test plan

  • npm audit in docs/ reports 0 vulnerabilities
  • astro build completes successfully, generating all 15 pages

Resolves 89 Dependabot alerts, all transitive deps of the pinned
Astro 5 / Starlight 0.35 / sharp 0.34 versions in the docs site
(vite, js-yaml, postcss, nanoid, h3, picomatch, devalue, and others).
Starlight 0.39 dropped the label+autogenerate sidebar shorthand, so
astro.config.mjs now wraps autogenerate entries in items arrays.
@martient martient self-assigned this Sep 27, 2026
@martient
martient merged commit 160208c into develop Sep 27, 2026
1 check passed
@martient
martient deleted the fix/docs-vulnerable-deps branch September 27, 2026 19:23
martient added a commit that referenced this pull request Sep 27, 2026
* fix: tag mapping

* fix: scope detection

* fix: tag behavior

* fix: prevent tag dry-run side effects

* Feat agent convention enforcement (#41)

* fix(core): preserve linked worktree discovery

* feat(core): stabilize agent json contract

* feat(core): add typed convention capabilities

* feat(core): add native convention hooks

* feat(agents): ship shared workflow plugin

* chore(agents): streamline convention guidance

* chore(core): satisfy strict clippy checks

* fix(docs): update starlight social config

* fix(core): preserve legacy branch defaults

* fix(hooks): support Windows release builds

* chore: bump version to 1.9.1

* docs(agents): add September 2026 agent compatibility audit

Audit the repository's agent-facing surfaces (AGENTS.md/CLAUDE.md, the
shared Codex/Claude plugin and skills, the schema capability contract,
generated hooks/CI, and the src/ai LLM integration) against the current
state of the agent-integration ecosystem.

Records 15 prioritised findings covering defects, compatibility gaps and
removals, plus two explicit non-recommendations.

* fix(cli): make agent invocations parse and fail machine-readably

Agents consistently failed against the CLI for three mechanical reasons,
each now reproduced and covered by tests/agent_ergonomics_tests.rs.

- --non-interactive and -q/--quiet are global, so they parse on either
  side of the subcommand. --verbose stays root-only: -v belongs to
  `branch --validate` and --verbose to `config validate|show`, and
  promoting it would steal both shorts. A test pins that trade-off.
- Argument-parse rejections now emit the machine envelope on stdout with
  code "invalid_usage" when --output json was requested, instead of an
  empty stdout and prose on stderr. Text mode is unchanged, and --help
  and --version keep their plain-text form and exit 0.
- schema capabilities cover the whole agent command surface (tag, bump,
  changelog, group-commit, config, packages, schema) and each entry now
  declares `mutating` and `requires_confirmation`, so an agent can reason
  about consent without pattern-matching command names.
- COMMITTY_NONINTERACTIVE is documented in AGENTS.md, CLAUDE.md and the
  agent-workflows reference; it makes flag placement irrelevant.

Also withdraws the audit's blanket "do not build an MCP server": that was
argued against eager-loading MCP, which the 2026-07-28 stateless spec has
moved off.

* fix(cli): global flag placement, usage envelope, capability surface

Second half of the agent-ergonomics change: main.rs flag globals and the
clap rejection handler, the expanded schema capability list, the
ergonomics test suite, and the docs describing flag placement.

* test(agents): cover agent CLI ergonomics and update the audit

Adds tests/agent_ergonomics_tests.rs (12 tests) covering flag placement
on both sides of the subcommand, the invalid_usage envelope on argv
rejection, the expanded capability surface with consent metadata, and
the presence of COMMITTY_NONINTERACTIVE in agent-facing docs.

Documents flag placement in the agent-workflows reference and records
the diagnosis in the audit, withdrawing its blanket recommendation
against an MCP server.

* docs(agents): document flag placement and the invalid_usage envelope

* docs(audit): record the CLI ergonomics diagnosis and withdraw the MCP verdict

* docs(agents): ban AI attribution trailers in commits and PRs

Tooling re-adds Co-Authored-By and Claude-Session by default, so the
prohibition has to live where agents read it rather than being restated
each session.

* fix(ai): make the Ollama provider work and stop sending an empty prompt

Four real defects from the agent audit, plus one withdrawn finding.

- Ollama requests now set `stream: false`. The endpoint defaults to
  streaming, so every previous call received newline-delimited events and
  died in LlmError::Parse before silently falling back to the default
  message. The provider has never worked.
- `format` moves from `options` to the top level, where Ollama reads it,
  and now carries a JSON Schema for AiCommitSuggestion rather than the
  string "json", so decoding is constrained instead of merely requested.
- The default (non-sensitive) AI path sent only the group name and an
  instruction not to reveal anything, leaving the model nothing to work
  from. It now sends a redacted shape summary: file count, extension
  histogram and top-level areas. No filename, no path below the first
  segment, and no file content is sent in either mode.
- `--ai-diff-lines-per-file` was declared, documented and accepted but
  never read. Removed, along with the dead LlmError::_Timeout variant and
  the commented-out LlmProvider enum.

The audit's P0-4 ("--ai silently ignored in --mode apply") was wrong and
is withdrawn: the apply arm has always had its own AI block. The real
problem there was duplication -- plan and apply carried identical copies
of group building and the AI block -- which is what made the misreading
possible. Both are extracted into build_groups() and ai_user_prompt().

Request bodies are now built by pure functions with unit tests, so the
wire format is verifiable without a network call.

* fix(ci): run checks on pull requests targeting nested branch names

The pull_request trigger filtered on branches: ["*"], and in Actions
branch filters `*` matches everything except `/`. Any pull request whose
base branch contains a slash therefore matched nothing and ran no checks
at all -- silently, since a workflow that does not trigger looks the same
as one with nothing to report.

That covers every stacked pull request in this series (all but the first
target a claude/... base and have zero check runs), and would equally
cover anything based on a feat/ or fix/ prefix.

`**` matches across slashes, which is what the filter meant.

* docs(agents): make AGENTS.md canonical and reach every runtime

AGENTS.md and CLAUDE.md were near-identical files kept in sync by hand;
the diff was two lines and had already started drifting (the trailer ban
landed in one of them only). AGENTS.md is now the single source and
CLAUDE.md is an @AGENTS.md import plus the one genuinely Claude-specific
line, which is the pattern the ecosystem settled on.

Two runtimes read neither file, so they now get thin pointers:
.github/copilot-instructions.md and GEMINI.md.

Skills are exposed at .agents/skills, the cross-tool discovery path that
Codex, Claude Code and Copilot CLI all resolve, via a symlink to the
canonical copy under plugins/committy/skills -- no duplication. A test
guards the symlink, because a checkout that materialises it as a plain
file would break discovery silently.

Skill frontmatter gains the spec's optional fields: license,
allowed-tools (so security-conscious runtimes can pre-authorise exactly
what each skill needs) and metadata carrying a skill version independent
of the CLI version.

committy-release referenced siblings with Codex's `$skill` syntax inside
a body shared by both runtimes; they are now named plainly and each
runtime resolves them its own way.

The COMMITTY_NONINTERACTIVE docs test now accepts a file that defers to
AGENTS.md rather than restating it, which is what the split requires.

* test(agents): cover the plugin manifests and skill frontmatter

AGENTS.md asks for public-seam tests over "machine output, exit codes,
Git behavior, hook protocol, and plugin manifests". The first four were
covered; nothing compiled or executed the manifests, so a typo in any of
the six JSON and Markdown files shipped silently.

Nine tests: both marketplace manifests parse and their declared sources
resolve to real directories carrying the expected plugin manifest; the
Claude and Codex plugin manifests agree on name, version and license;
the Codex skills path resolves; skill frontmatter uses only the keys the
Agent Skills spec allows and carries the two required ones; a skill's
frontmatter name matches its directory; descriptions carry a trigger
rather than just a capability; no skill body uses runtime-specific
invocation syntax, since that body is shared by Codex and Claude Code;
and every skill is reachable from the cross-tool .agents/skills path.

Each assertion was mutation-checked -- a bogus frontmatter key and a
marketplace source pointing at a missing directory both fail with the
message that names the problem.

* chore(ci): modernise the generated workflow and tighten the agent guardrail

The workflow `hooks install --with-ci` writes for users pinned
actions/checkout@v4 and ran `cargo install committy --locked` on every
pull request -- a multi-minute Rust build per run, uncached, with no
toolchain pin. It now pins checkout@v5, pins the toolchain, caches the
build, and takes non-interactive mode from COMMITTY_NONINTERACTIVE so the
command cannot be broken by flag placement. The repository's own three
workflows carried the same stale checkout pin and are bumped too.

The local PreToolUse guardrail grepped the raw tool input for nine
substrings, which made it both over- and under-inclusive. Over: it
matched the *text* of a command rather than an invocation, so writing
documentation that merely named a push was refused -- including,
circularly, the heredoc that would have replaced this file -- and it
refused the push-with-upstream form the repository's own agent
instructions require. Under: two spaces between git and the subcommand,
an `env X=1` prefix, or `git -C .` all slipped through, so it never was
a control. It now resolves the command field (with a jq-free fallback),
discards heredoc bodies, returns early unless git is invoked, and refuses
only what destroys history or uncommitted work: unguarded force-push
(--force-with-lease allowed), hard reset, forced clean, forced branch
delete, and bulk working-tree discards. Plain push is left to the
pre-push hook, which is policy-engine backed.

PARTIALLY VALIDATED: the full behaviour matrix could not be run from the
session that wrote the hook, since executing one's own permission hook is
refused as self-modification. Observed in normal use: ordinary commands
and a plain push pass, and `git branch -D` was correctly refused. The
remaining deny cases are unverified and a reviewer should confirm them.

Also removes the roadmap page, which carried frontmatter and no body and
published as an empty page.

* test: stop the tag signing test depending on ambient git config

test_tag_respects_repo_git_overrides_and_cli_can_override_them asserts
that a repo-configured gpg.program is consulted, by pointing it at a
script that always fails and expecting the tag creation to fail with it.

That only holds when gpg.format is openpgp. On a machine whose global
config sets gpg.format=ssh, git routes signing to gpg.ssh.program, the
stub never runs, the tag is signed successfully by the ssh signer, and
the assertion evaporates -- the test fails claiming Committy ignored the
override when Committy had passed it through correctly. Verified: the tag
git produced in that environment carried a valid SSH signature.

The test now pins gpg.format=openpgp alongside the other overrides, so it
exercises the path it means to exercise regardless of the host.

Separately, the three config::hierarchy tests call MergedConfig::load,
which reads the user config from $HOME. The tests in config.rs mutate
HOME and are marked #[serial]; these were not, so they could read a
half-written or relocated user config while one of those held it, and
MergedConfig::load(...).unwrap() would panic. They are now serial too.

Caveat on that second change: it closes a real race but is unproven. The
failure was observed once, and did not recur in thirteen subsequent runs
(eight lib-only, five full) even with the fix reverted, so causality is
reasoned rather than demonstrated. It is cheap and correct regardless --
a test reading global state that other tests mutate belongs in the same
serial group.

* test(hooks): add a behaviour matrix for the guardrail, and fix what it caught

The rewritten guardrail shipped unvalidated: nothing executed it, so its
deny rules were assumed rather than checked. That matters more than for
ordinary code, because a guardrail that silently stops refusing looks
exactly like one that works.

Twelve tests feed the hook the same JSON the harness does and assert the
exit code. Writing them found three defects in the hook, all now fixed:

- `git push -f origin main` was not refused. The force check was one
  ordered regex whose `-f` alternative required a preceding space, which
  the `push ` match had already consumed. Force detection is now two
  independent conditions -- a push, and a force flag anywhere -- so flag
  order and adjacency stop mattering.
- A heredoc feeding an interpreter was dropped along with every other
  heredoc body, so `bash <<EOF` / `git reset --hard` / `EOF` sailed
  through. Heredoc bodies are still discarded when the opener writes to a
  file, but scanned when it names a shell or interpreter.
- Quoted prose naming a dangerous command is still refused. This one is
  deliberate and now recorded as a decision: stripping quoted segments
  would fix it and would also let `bash -c "git reset --hard"` through,
  and for a guardrail a false refusal costs far less than a false pass.

Coverage includes the variants the previous substring hook missed
entirely -- doubled spaces, a leading env assignment, `git -C .`, and a
git call after `&&`.

* fix(release): stop shipping binaries that report the previous version (#50)

* ci(release): fail the release when Cargo.toml disagrees with the tag

v1.10.0 was tagged on a tree whose Cargo.toml still said 1.9.1, so every
published binary reported the old version to --version and self-update kept
offering the same release forever.

A verify-version job now compares the Cargo.toml version with the tag name and
both the release creation and the build matrix wait on it, so a mismatched tag
fails before any release or asset is published.

* feat(tag)!: bump version files by default when tagging

Tagging without --bump-files left the version files stale, which is how a
release can be cut whose binaries report the previous version. Version files
are now bumped and committed unless --no-bump-files is passed; --bump-files is
still accepted so existing scripts keep working, and passing both is an error.

Two bugs surfaced by making this the default are fixed alongside it:

- version files that already carried the new version were reported as updated,
  and the empty commit that followed aborted the whole tag operation
- multi-package repositories staged the bumped files but never committed them,
  so their tags pointed at a commit with the old versions

* chore(release): align version files with published v1.10.0

* fix(docs): update vulnerable astro, starlight, and sharp dependencies (#51)

Resolves 89 Dependabot alerts, all transitive deps of the pinned
Astro 5 / Starlight 0.35 / sharp 0.34 versions in the docs site
(vite, js-yaml, postcss, nanoid, h3, picomatch, devalue, and others).
Starlight 0.39 dropped the label+autogenerate sidebar shorthand, so
astro.config.mjs now wraps autogenerate entries in items arrays.

---------

Co-authored-by: Claude <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