Commit-frequency enforcement across agents#13
Merged
Conversation
PostToolUse hook on Edit/Write/MultiEdit/NotebookEdit that counts working-tree changes after each tool call. When the count crosses a threshold (default 4, override via WEBJS_COMMIT_NUDGE_THRESHOLD), injects a reminder into the model's context via hookSpecificOutput. Soft nudge by design. Does not block edits. Skipped on main/master (guard-branch-context.sh handles that case) and outside a git work tree. Threshold of 4 catches the common batching failure mode without firing for trivial work. Motivation: I just shipped PR #12 with too many changes in too few commits. The text guidance in AGENTS.md is easy to ignore. A hook is harder.
Mirrors the framework-repo hook into the scaffold templates so end-user webjs apps inherit the same commit-frequency enforcement out of the box. Three pieces: * templates/.claude/hooks/nudge-uncommitted.sh: copy of the framework hook * templates/.claude/settings.json: registers it on PostToolUse for Write/Edit/MultiEdit/NotebookEdit * lib/create.js: adds the new hook to the chmod list so scaffolded apps get the executable bit (the templates directory itself does not preserve +x)
Replaces the soft 'commit and push freely' wording with explicit directives: commit per logical unit, do not pile changes, push after each commit, and respect the nudge-uncommitted hook reminder at threshold 4. Reframes 'meaningful commit messages' to say the body explains the reason rather than the diff. Driven by the failure mode in PR #12, where 8 logical units collapsed into 1 batched diff before being split at the end.
…NS/cursorrules/windsurfrules Aligns the four scaffold-template agent rule files with the new framework AGENTS.md wording: * logical unit defined explicitly (one feature, one fix, one rename, one doc rewrite) * hard limit of 5+ unstaged files spanning different concerns before commit is mandatory * reference to the .claude/hooks/nudge-uncommitted.sh hook so Claude Code users see it cited in the prose they read, and Cursor/Windsurf users have an explicit self-enforce directive since their agents do not consume Claude Code hooks Copilot's instructions file lands in the next commit (hook fired mid-edit at threshold 4, respecting it).
…ons.md Completes the cross-agent rollout from the previous commit. Copilot has no hooks API; the rule self-enforces via prose. Same logical-unit definition and 5-file hard limit as the other agent rule files.
Tool-agnostic git-level enforcement. Fires on every commit regardless of agent (Claude, Cursor, Windsurf, Copilot, human). Blocks commits that fail tests or convention checks. Skipped on fresh clones before npm install (when the CLI is not yet on PATH). To bypass in emergencies: git commit --no-verify.
Adds the same commit-frequency enforcement to scaffolded apps for Gemini CLI users. The hook output shape (hookSpecificOutput. additionalContext) is identical to Claude Code's, so the body is nearly a copy of the Claude version. Also fixes a small omission from the earlier 'scaffold nudge hook' commit: '.claude/hooks/nudge-uncommitted.sh' was created in templates/ but never added to the templateFiles copy list in create.js, so it would not have shipped to scaffolded apps. Add it to the list now alongside the Gemini files. Doc reference: https://geminicli.com/docs/hooks/reference/
Cursor 1.7+ ships a hooks system at .cursor/hooks.json. The afterFileEdit event fires after Cursor's edit/write tools and accepts a top-level additional_context field (snake_case) to inject a soft reminder into the agent's context. Same threshold + message as the Claude Code and Gemini CLI hooks. Skipped on main/master and outside a git work tree. Doc reference: https://cursor.com/docs/hooks
Scaffolded apps now ship hooks for Claude Code, Gemini CLI, and Cursor 1.7+. Other agents (Windsurf, Copilot, OpenCode, Antigravity) fall back to text rules. Records this matrix in both templates/AGENTS.md (for end-user webjs apps) and root AGENTS.md (for framework devs). Notes the rationale for each fallback: * Windsurf post-write hooks cannot inject context to the agent * Copilot has no hooks API * OpenCode has plugins (TS, planned for follow-up) * Antigravity has no hooks API Pre-commit (.hooks/pre-commit) is the tool-agnostic backstop: runs webjs test + webjs check on every commit regardless of which agent (or human) is making it.
…d app Mirrors the framework-repo prose hook into scaffolded apps so end users get the same enforcement of AGENTS.md invariant 11 (no em-dashes, no hyphen-as-pause, no semicolon-as-pause, no xyz():-then-prose). The rule is already documented in templates/AGENTS.md and the per-agent rule files; the hook adds programmatic enforcement for Claude Code users. Three pieces: * templates/.claude/hooks/block-prose-punctuation.sh: copy of the framework hook * templates/.claude/settings.json: registers it on PreToolUse for Write/Edit/MultiEdit/NotebookEdit/Bash * lib/create.js: extends templateFiles copy list and chmod loop
…ain locally This hook prompted on every 'git merge' and every 'git push ... main' through Claude's Bash tool. That matches the framework dev's preference for a strict GitHub-PR-only workflow, but it is the wrong default for end users. Scaffolded webjs apps may use GitLab, Bitbucket, plain git, or just merge locally without a forge at all. End-user enforcement against main is now layered as: * .hooks/pre-commit (tool-agnostic) blocks direct commits to main * .claude/hooks/guard-branch-context.sh prompts before editing on main Both of those work with any git workflow including local merges and direct pushes to main. The framework repo keeps its own copy of the merge guard if the user wants to formalize their PR-only workflow there.
vivek7405
added a commit
that referenced
this pull request
May 19, 2026
Mirrors what scaffolded webjs apps get, adapted for the framework repo's own test runner. Two gates: 1. Block direct commits to main/master (use a feature branch) 2. Run npm test, refuse the commit if it fails Adds a 'prepare' script to root package.json that sets core.hooksPath to .hooks so the hook activates on npm install for any contributor who clones the repo. To bypass in emergencies: git commit --no-verify. Driven by an audit gap: the scaffold templates have shipped a pre-commit since PR #13 merged, but the framework repo itself had no equivalent, so my own commits were not gated by tests.
This was referenced May 19, 2026
vivek7405
added a commit
that referenced
this pull request
May 21, 2026
Third slice. Wires the slot runtime into the WebComponent lifecycle and
refines render-client.js's slot bind so fallback content is captured
once at compile time and cloned freshly per instance.
component.js:
- Light-DOM activation now runs in two phases:
Phase 1 (before _performRender): if not hydrating, call
captureAuthoredChildren(host) to move authored children into the
slot state's assignment table. The renderer's replaceChildren()
call would otherwise destroy them on first render.
Phase 2 (after _performRender, slots are now live DOM): if the
host was hydrating, call adoptSSRAssignments(host) to record
SSR-placed children so the first projection pass is a no-op.
Then call attachSlotObservers(host) so future authored-child
mutations and slot-name changes drive incremental projection.
- New __isHydrating() helper inspects this.firstChild for the
framework's <!--webjs-hydrate--> marker and records the result
on __hydratedAtActivate so phase two can branch correctly.
- disconnectedCallback now calls detachSlotObservers(host) for
light-DOM hosts. The per-host slot state (assignment table,
pending fragments, last snapshots) is preserved across disconnect,
so a re-attached element picks up where it left off.
render-client.js:
- discoverSlots() now MOVES the slot's authored children into a
fallbackTemplate DocumentFragment stored on the SLOT
PartDescriptor. The cached templateEl's slot becomes empty, so
every clone starts empty too. This eliminates the cloning-the-
fallback-out-of-the-slot dance the previous bind step did.
- bindPart for slot now clones the descriptor's fallbackTemplate
into a per-instance holding fragment and stamps it on the slot
via SLOT_FALLBACK_FRAG for slot.js to swap in. The slot itself
is left untouched at bind time, which makes hydration trivially
correct (SSR-projected children stay in place; the slot-part
just sets up its fallback supply for later transitions).
- PartDescriptor typedef gains an optional fallbackTemplate field.
Verified across 115 existing core unit tests (component, render-client,
render-server, directives, registry, css, html, context, task,
suspense, repeat, testing). All pass.
What remains: render-server.js injectDSD upgrade for SSR slot
substitution (Task #13), then the 62-case test suite (Task #14),
then docs (Task #15).
vivek7405
added a commit
that referenced
this pull request
May 21, 2026
Commit-frequency enforcement across agents
vivek7405
added a commit
that referenced
this pull request
May 21, 2026
Mirrors what scaffolded webjs apps get, adapted for the framework repo's own test runner. Two gates: 1. Block direct commits to main/master (use a feature branch) 2. Run npm test, refuse the commit if it fails Adds a 'prepare' script to root package.json that sets core.hooksPath to .hooks so the hook activates on npm install for any contributor who clones the repo. To bypass in emergencies: git commit --no-verify. Driven by an audit gap: the scaffold templates have shipped a pre-commit since PR #13 merged, but the framework repo itself had no equivalent, so my own commits were not gated by tests.
vivek7405
added a commit
that referenced
this pull request
May 21, 2026
For agents without a programmatic hooks API (Windsurf, Copilot) and as a baseline source of truth for all agents: * .cursorrules: Cursor's rule file * .windsurfrules: Windsurf's rule file * .github/copilot-instructions.md: GitHub Copilot's rule file * .github/pull_request_template.md: PR template * .editorconfig: editor settings (indent, line endings) All copied verbatim from packages/cli/templates/, including the strengthened commit-frequency wording and hook references from PRs #12 and #13.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds programmatic enforcement of the "commit per logical unit" rule for AI agents that support hooks. Falls back to text rules for those that don't.
Hook coverage matrix
PostToolUsehook.claude/hooks/nudge-uncommitted.shAfterToolhook.gemini/hooks/nudge-uncommitted.shafterFileEdithook.cursor/hooks/nudge-uncommitted.sh.windsurfrules.github/copilot-instructions.mdAGENTS.mdAGENTS.mdTool-agnostic backstop
.hooks/pre-commitnow also runswebjs testandwebjs check. Fires on every commit regardless of which agent or human made it. Skipped if the CLI isn't installed yet, e.g. fresh clone beforenpm install.Behavior
Each hook counts
git status --porcelainafter an edit. When the count is at least 4 (override viaWEBJS_COMMIT_NUDGE_THRESHOLD), it injects a soft reminder into the agent's context. Output shape per tool:hookSpecificOutput.additionalContextfield.additional_contextfield.Hooks are soft, they exit 0 always and never block. Skipped on
main/mastersinceguard-branch-context.shhandles that case.Commits in this branch
Nine commits, each a single logical unit. Highlights:
lib/create.jsAGENTS.md,CONVENTIONS.md,.cursorrules,.windsurfrules,copilot-instructions.mdTest plan
WEBJS_COMMIT_NUDGE_THRESHOLD=2against a temp dirty repo. Each emits the JSON shape its tool expects.npm testpasses, 896 of 896.webjs createand connecting Gemini or Cursor. Not done in this PR.