Auto-commit + auto-close generator drift after coily exec #48

Open
opened 2026-05-23 20:54:02 +00:00 by coilysiren · 0 comments
Owner

Originally filed by @coilysiren on 2026-05-11T04:58:29Z - https://github.com/coilysiren/coily/issues/113

Problem

Scheduled coily exec daily-* sessions choke on repo_verb_dirty whenever an adjacent generator leaves the tree dirty between runs. The high-frequency cause (mcporter .d.ts timestamp churn) is fixed upstream in coilyco-ai#277, but real schema drift (mcporter version bump, new tool, transport change) still trips the gate.

The right shape, per kai's design call: have coily exec itself auto-commit deterministic generator outputs after a successful verb, so the tree is clean by the time the next scheduled task fires. Audit trail preserved via a freshly-filed GitHub issue that the same commit closes — keeps the "every commit closes an issue" rule airtight, no hook change needed.

Config surface

Repo-wide block in .coily/coily.yaml, applies to every verb under commands::

auto-commit:
  paths:
    - mcp-servers/*.d.ts
commands:
  daily-update:
    run: python3 scripts/daily-update.py
    description: ...
  • paths - one-level glob list (filepath.Match). No ** support, punt.
  • No prefix field (issue handles the hook).
  • No push toggle (always pushes).
  • Absent block = today's behavior, no auto-commit.

Per-verb flow

After coily exec <verb> returns 0:

  1. git status --porcelain. Empty -> return as today.
  2. Non-empty -> match every dirty path against auto-commit.paths. Any unmatched -> log auto-commit skipped: unexpected paths <list>, return. Next verb trips repo_verb_dirty as today (surprise drift still gets human eyes).
  3. All matched:
    1. gh issue create on the current repo with the chore body. Capture #N.
    2. git add <matched paths>.
    3. git commit -m "<message including closes #N>" - passes existing commit-msg hook unmodified.
    4. If commit fails (leak-check, validate-skills, trufflehog, anything else in the pre-commit chain): gh issue close #N with a comment naming the failing hook and the audit row, abort, leave tree dirty.
    5. git push. GitHub auto-closes #N on receive.
  4. Print auto-committed <SHA>, closed #N, <K> files to stdout. Not silent.

Issue + commit shape

Issue title: chore: refresh <first-glob-match> after <verb> auto-commit

Issue body:

Auto-filed by `coily exec <verb>` (audit row: <run-id>).

Modified paths (<K>):
- mcp-servers/eco.d.ts        (+12 -1)
- mcp-servers/gcal.d.ts       (+0 -0, header only)
- ...

Closed by the same commit that filed this.

Label auto-filed so the daily-backlog routine can filter these out of human triage.

Commit message:

chore: refresh mcp-server schemas after daily-update auto-commit

Modified: eco.d.ts, gcal.d.ts, gmail.d.ts, phoenix.d.ts, playwright.d.ts, terraform.d.ts

closes #N

Audit-log: coily/<run-id>

Diff stats in the issue body are the audit surface: if a "header refresh" issue is closed with a 200-line diff, it shows up in the closed-issue stream and can be investigated.

Failure semantics

  • gh issue create fails (offline, rate-limited, auth) -> no commit, tree stays dirty, next verb trips the gate. Same recovery as today.
  • Pre-commit hook fails -> close the issue with explanation, abort. No leaked open issues.
  • git push fails (offline) -> commit landed locally, issue still open on GitHub. Next push from any verb carries it and GitHub closes #N.
  • Verb itself fails -> no auto-commit. Tree stays as evidence.

Safety properties preserved

  • Verb failure -> tree stays dirty as evidence.
  • Unexpected file modified -> tree stays dirty, next verb trips the gate.
  • All commits still go through commit-msg, leak-check, trufflehog, validate-skills.
  • --audit-override-dirty semantics unchanged.
  • --no-verify stays banned.

Hook change

None. The commit honestly closes an issue, so scripts/check-commit-closes-issue.py is unchanged across all sibling repos. The "every commit closes an issue" rule stays airtight.

Rollout

  1. Land this change on main.
  2. Wait on the homebrew release pipeline per kai-brew-release. No locally-built binary.
  3. After brew upgrade coily, add the auto-commit block to coilyco-ai's .coily/coily.yaml.
  4. Verify: coily exec daily-update lands a chore commit + closed issue, second run is no-op.

Done when

  • coily exec <verb> with auto-commit.paths configured leaves the tree clean after success when only allowlisted paths are dirty.
  • A chore: commit lands on main with an Audit-log: trailer and a closes #N keyword.
  • Issue #N is auto-closed by GitHub on push, labeled auto-filed.
  • Unexpected paths still trip repo_verb_dirty on the next verb (safety property preserved).
  • Two consecutive coily exec daily-update runs against coilyco-ai produce exactly one chore commit (idempotence).
  • coilyco-ai#277 - the timestamp-stripping half of the same problem class.
_Originally filed by @coilysiren on 2026-05-11T04:58:29Z - [https://github.com/coilysiren/coily/issues/113](https://github.com/coilysiren/coily/issues/113)_ ## Problem Scheduled `coily exec daily-*` sessions choke on `repo_verb_dirty` whenever an adjacent generator leaves the tree dirty between runs. The high-frequency cause (mcporter `.d.ts` timestamp churn) is fixed upstream in coilyco-ai#277, but real schema drift (mcporter version bump, new tool, transport change) still trips the gate. The right shape, per kai's design call: have `coily exec` itself auto-commit deterministic generator outputs after a successful verb, so the tree is clean by the time the next scheduled task fires. Audit trail preserved via a freshly-filed GitHub issue that the same commit closes — keeps the "every commit closes an issue" rule airtight, no hook change needed. ## Config surface Repo-wide block in `.coily/coily.yaml`, applies to every verb under `commands:`: ```yaml auto-commit: paths: - mcp-servers/*.d.ts commands: daily-update: run: python3 scripts/daily-update.py description: ... ``` - `paths` - one-level glob list (`filepath.Match`). No `**` support, punt. - No `prefix` field (issue handles the hook). - No `push` toggle (always pushes). - Absent block = today's behavior, no auto-commit. ## Per-verb flow After `coily exec <verb>` returns 0: 1. `git status --porcelain`. Empty -> return as today. 2. Non-empty -> match every dirty path against `auto-commit.paths`. Any unmatched -> log `auto-commit skipped: unexpected paths <list>`, return. Next verb trips `repo_verb_dirty` as today (surprise drift still gets human eyes). 3. All matched: 1. `gh issue create` on the current repo with the chore body. Capture `#N`. 2. `git add <matched paths>`. 3. `git commit -m "<message including closes #N>"` - passes existing commit-msg hook unmodified. 4. If commit fails (leak-check, validate-skills, trufflehog, anything else in the pre-commit chain): `gh issue close #N` with a comment naming the failing hook and the audit row, abort, leave tree dirty. 5. `git push`. GitHub auto-closes `#N` on receive. 4. Print `auto-committed <SHA>, closed #N, <K> files` to stdout. Not silent. ## Issue + commit shape Issue title: `chore: refresh <first-glob-match> after <verb> auto-commit` Issue body: ``` Auto-filed by `coily exec <verb>` (audit row: <run-id>). Modified paths (<K>): - mcp-servers/eco.d.ts (+12 -1) - mcp-servers/gcal.d.ts (+0 -0, header only) - ... Closed by the same commit that filed this. ``` Label `auto-filed` so the daily-backlog routine can filter these out of human triage. Commit message: ``` chore: refresh mcp-server schemas after daily-update auto-commit Modified: eco.d.ts, gcal.d.ts, gmail.d.ts, phoenix.d.ts, playwright.d.ts, terraform.d.ts closes #N Audit-log: coily/<run-id> ``` Diff stats in the issue body are the audit surface: if a "header refresh" issue is closed with a 200-line diff, it shows up in the closed-issue stream and can be investigated. ## Failure semantics - `gh issue create` fails (offline, rate-limited, auth) -> no commit, tree stays dirty, next verb trips the gate. Same recovery as today. - Pre-commit hook fails -> close the issue with explanation, abort. No leaked open issues. - `git push` fails (offline) -> commit landed locally, issue still open on GitHub. Next push from any verb carries it and GitHub closes `#N`. - Verb itself fails -> no auto-commit. Tree stays as evidence. ## Safety properties preserved - Verb failure -> tree stays dirty as evidence. - Unexpected file modified -> tree stays dirty, next verb trips the gate. - All commits still go through commit-msg, leak-check, trufflehog, validate-skills. - `--audit-override-dirty` semantics unchanged. - `--no-verify` stays banned. ## Hook change None. The commit honestly closes an issue, so `scripts/check-commit-closes-issue.py` is unchanged across all sibling repos. The "every commit closes an issue" rule stays airtight. ## Rollout 1. Land this change on main. 2. Wait on the homebrew release pipeline per `kai-brew-release`. No locally-built binary. 3. After `brew upgrade coily`, add the `auto-commit` block to coilyco-ai's `.coily/coily.yaml`. 4. Verify: `coily exec daily-update` lands a chore commit + closed issue, second run is no-op. ## Done when - `coily exec <verb>` with `auto-commit.paths` configured leaves the tree clean after success when only allowlisted paths are dirty. - A `chore:` commit lands on main with an `Audit-log:` trailer and a `closes #N` keyword. - Issue `#N` is auto-closed by GitHub on push, labeled `auto-filed`. - Unexpected paths still trip `repo_verb_dirty` on the next verb (safety property preserved). - Two consecutive `coily exec daily-update` runs against coilyco-ai produce exactly one chore commit (idempotence). ## Related - coilyco-ai#277 - the timestamp-stripping half of the same problem class.
coilysiren added
P3
and removed
P2
labels 2026-05-31 06:59:49 +00:00
Sign in to join this conversation.
No labels
P0
P1
P2
P3
P4
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-bridge/coily#48
No description provided.