Skip to content

chore(release): promote v0.6.21 to main - #60

Merged
bacsystem merged 12 commits into
mainfrom
develop
Jul 22, 2026
Merged

chore(release): promote v0.6.21 to main#60
bacsystem merged 12 commits into
mainfrom
develop

Conversation

@bacsystem

Copy link
Copy Markdown
Owner

Summary

Promotes develop to main: v0.6.21.

  • writeState()/deleteState()/appendLedger() now verify against real repo state before writing (fixes a safety-classifier false positive), and the state-write agent self-corrects stale-but-genuine entries instead of only refusing.
  • requestWriteState()/appendLedger() coalesce concurrent calls into one agent and log how many got batched; bookkeeping agents now run at effort: 'low'.
  • runTask fast-exits tasks already merged into the integration branch, while still requiring real commits of its own so an empty stub branch implements normally.
  • bin/review-package.js: raised execFileSync maxBuffer to 1GB.
  • cys:plan self-review checklist: added caller-cardinality and spec-field-literalness checks.

Full details in CHANGELOG.md.

Merge

Use "Create a merge commit" (squash is disabled repo-wide) to keep shared ancestry with develop intact.

bacsystem and others added 12 commits July 21, 2026 07:35
Default Node execFileSync buffer (1MB) is too small for a real diff
package spanning many files (hit during the bs-inventory pilot's
Task 13, a 207-file MUI Premium/Pro removal) — git commands would
throw ENOBUFS/maxBuffer exceeded instead of returning the diff.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
The 2026-07-18 fix for the safety-classifier blocking state-write calls
("frame the content as already-verified bookkeeping") turned out not to
resolve the problem — it's exactly that framing that kept getting
flagged as Instruction Poisoning across two real bs-inventory pilot runs
(2026-07-20/21). An isolated agent with no memory of the run has no way
to confirm "trust me, this already happened," which reads exactly like
an injection attempt.

Evidence from those same runs: agents that independently verified
against real repo state (git log, task reports) before writing passed
the classifier without issue. So writeState()/deleteState()/
appendLedger() now instruct the agent to verify the content against the
actual repo (git log, git merge-base, .cys/task-<id>-report.md) before
writing, with an explicit "don't write, report the discrepancy instead"
escape hatch if verification fails — turning the task from "copy this
claim" into "verify and record," which is the pattern that already
worked in practice.

Full context in .cys/pending.md (reopened the 2026-07-18 entry rather
than filing a new one).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
…f-review checks

Two real bugs shipped past cys:plan's existing self-review checklist in
the bs-inventory pilot (2026-07-20/21), both caught only by the
executor's adversarial reviewer, not by writing the plan itself:

- BuildKardex used one shared balance accumulator, tested with a single
  product/warehouse; two other tasks' sample code fed it a whole
  tenant's multi-product/multi-warehouse movements, silently mixing
  balances — invisible without tracing the real call sites.
- PLE field 4 used the warehouse's internal ID instead of
  Warehouse.rucEstablishmentCode, which the design spec named explicitly
  — written from a general sense of "the warehouse" rather than the
  spec's literal field mapping.

Neither "does every function have a test" nor "do types match across
tasks" catches either class, so both are now explicit self-review steps.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
…fusing

Fixing the classifier false-positive (previous commit) surfaced a real
follow-on gap: writeState()'s snapshot is built when the write is
queued, but enqueueMainRepo serializes it behind other real repo work,
so by the time the agent actually runs, the content can be genuinely
stale — not fabricated, just overtaken by real progress. The agent was
correctly verifying this (as designed) but only knew how to refuse and
report, with no automatic retry anywhere in the script — so state.json
stayed frozen at whatever the last accepted write was, observed in a
live bs-inventory pilot run staying pinned at the very first snapshot
(before any task had even started) for the rest of the run.

writeState() now tells the agent that staleness alone isn't a red flag,
and to correct the specific task entries itself from what it verifies
(real branch/SHA for done tasks; status "in_progress" *and* a "phase"
for started-but-unmerged tasks — a status with no phase is an
incomplete correction) rather than stopping at "this doesn't match,
I won't write it." Refusal is now reserved for entries that look
fabricated outright (a "done" SHA that doesn't exist anywhere in the
repo's history), not merely out of date.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
settle() and markInProgress() each called writeState() directly, so
every task transition dispatched its own [state] agent — with several
tasks settling close together (a common pattern: every dependency-free
task starting around the same time) this meant one agent per
transition, even though a single fresh write right after the cluster
would capture the same information.

requestWriteState() now coalesces: if a write is already in flight when
a new request arrives, it just flags "there's more to record" instead
of spawning another agent; the in-flight write's own loop notices the
flag once it finishes and does one more pass (reading fresh state at
that point) before settling. Crash-resume safety is unchanged — it still
writes at least once per settling wave — but the agent count drops with
how much settling clusters in real runs.

Also fixed a duplicated sentence left over from the previous commit's
writeState() prompt edit (harmless but confusing repeated text).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
Same pattern as the state-write coalescing (previous commit): each
appendLedger() call dispatched its own [ledger] agent, so several
lines queued close together (e.g. multiple tasks failing/conflicting
around the same time) fired one agent per line. appendLedger() is now
a thin coalescing wrapper — concurrent calls join a pending batch, and
one [ledger] agent appends (and spot-checks) all of them together via
the new appendLedgerBatch(). Public call sites are unchanged (still
appendLedger(line)).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
…agent

Both requestWriteState() and appendLedger() now log a line
("[state] agrupó N pedidos..." / "[ledger] agrupó N líneas...")
whenever a batch actually groups more than one request, so it's
visible in the run's own log output — not just inferable from
comparing agent counts — whether the coalescing is doing anything in
a given run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
state/ledger/state-clear agents inherit the session's reasoning effort,
spending implementation-grade reasoning on mechanical work (verify git,
write a JSON file, append log lines). Real pilot data (bs-inventory,
2026-07-21): bookkeeping agents accounted for 35 of 77 dispatched
agents. effort:'low' cuts their per-call cost without touching the
quality-bearing agents (implement/review/fix/merge/final-review).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
The Workflow resume cache invalidates by prefix — any edit to the first
agent call's prompt (e.g. the writeState fixes) re-runs EVERYTHING after
it live. Combined with mid-run stop/resume cycles, already-finished
tasks kept getting re-dispatched from scratch: the bs-inventory pilot
(2026-07-21) re-invoked tasks 4/5/6 three to five times each, and every
re-invocation paid a full re-implementation check (testcontainers suite
included), a fresh adversarial review, and a merge attempt — 63 review
agents and 57 merge agents for a 10-task run, with three implementers
independently reporting "this task was already done, why was I
dispatched?" in their concerns.

Two-part fix:
- The implementer prompt now opens with a FAST-EXIT CHECK: if branch
  task-N exists AND is already an ancestor of the integration branch,
  report alreadyMerged: true immediately (two read-only git commands,
  no test re-runs, no worktree).
- runTask short-circuits on impl.alreadyMerged: settle(done) directly,
  skipping the review and merge stages entirely — there is nothing new
  to review or merge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
… work

Edge case found before it ever ran: a task branch created right before
a run was cut off (e.g. session limit hitting between branch creation
and the first commit) points at the integration tip with zero commits
of its own — trivially "an ancestor," which the fast-exit would have
read as "already merged" and silently skipped the task's entire
implementation. Real instance: bs-inventory's task-10 stub after the
2026-07-21 session-limit cutoff.

The check now also requires the branch to carry real commits of its own
in the integration branch's history; an empty stub gets deleted and the
task implements normally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145sjAtP5b5kvhR1XCgg3K3
…on-prompt

fix(runTask): fast-exit must not treat an empty stub branch as merged work
@bacsystem bacsystem self-assigned this Jul 22, 2026
@bacsystem
bacsystem merged commit 0272580 into main Jul 22, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant