Skip to content

v0.33.0

Choose a tag to compare

@github-actions github-actions released this 04 Aug 11:45
ec3ccb2

The performance & autonomy overhaul — the tool stops feeling slow and stalled. A cold
engineering review (three parallel deep-dives over the work loop, the plan→expert flow and the
architecture) found the stalls were structural: an unbounded CI wait, a 6-minute reviewer poll
paid on every PR, a dead-code run budget, no automatic epic progression, zero visible signal
when a run stopped, ~50–140 uncached GitHub calls per issue, a ~1.3 s interpreter spawn on every
tool call, ~72 KB of always-loaded instructions, and a telemetry subsystem blind to time. Epic
#561 fixed all fifteen findings: #562#576, PRs #577#591, every behavioral change with tests
verified by breaking the code, and every PR through the review gate (Codex external review in
rounds while quota lasted — 40+ findings fixed across the batch — then the sanctioned
self-review fallback, stated on each record).

Added

  • The product agenda policy is written down (#576, closing epic #561's list). CONTRIBUTING.md
    now states the rebalance rule the architecture review argued for: releases should carry
    user-facing value (the last measured week ran 8:1 self-directed); before building a new
    self-control mechanism, ask what measurement would make it unnecessary; and field evidence
    (/board telemetry, now with durations) outranks introspection when picking the next piece
    of work. A policy, not a gate — deliberately unenforced.

Changed

  • Board-Work.ps1 sheds its inline job scheduler and process killer (#575, part of epic
    #561; first slice of the monolith split). The 3,176-line dispatcher — 18.7% of all script
    code — contained a machine-capacity governor and a process-tree supervisor a board tool has
    no business holding inline. Both clusters moved verbatim into part files
    (BoardWork.Capacity.ps1: capacity snapshot, dispatch plan, fleet slot pacing, dispatch
    governor; BoardWork.Processes.ps1: pid→parent map, descendant walk, guarded tree kill,
    orphan detection and reaping), dot-sourced before the test guard so every caller and test
    sees the exact same surface. Behavior-preserving by construction and by evidence: 371 tests
    across the Board-Work, Board-Doctor and lint suites stay green with zero changes to them.
    Further slices (session registry, worktree launch) can now follow the same pattern.

Added

  • .agentic-board/ finally has a garbage collector (#574, part of epic #561). The state dir
    had accumulated 48 files across the project's entire life — briefings and launch scripts for
    issues #123#532, signal markers, compaction snapshots — because nothing ever cleaned it.
    Clear-AbiosState.ps1 reaps only regenerable per-run debris (briefings, launch scripts,
    expert briefs, per-issue logs, signal markers, snapshots) under three pinned rules: a live
    session protects its files at any age, files younger than -MaxAgeDays (14) stay, and durable
    records (sessions history, run-ledger marker, contract, denial log, fleet files) plus anything
    unrecognized are never touched — unknown is somebody's state, not deletable. Plan-only by
    default, -Force executes, every reap entry carries its reason. This is the GC half of the
    "one run, one record" direction; the record half was seeded by #568's sessions-history.jsonl
    archive (started/ended/outcome). Verified by 9 Pester tests.

Changed

  • The always-loaded instruction payload shrank ~60% (#573, part of epic #561). Every
    /board invocation loaded ~72 KB of instructions (≈18k tokens, ~9% of a 200k window) before
    reading a single line of the actual issue — commands/board.md described all 22 verbs in full
    and projects-admin/SKILL.md duplicated the three biggest. The ten heavyweight verbs (work,
    plan, fill, field, changelog, handoff, doctor, cerrar-ciclo, telemetry, triage) now live in
    per-verb reference files loaded on demand when that verb runs; the command keeps the menu,
    one-line routing and the safety rules, and the skill keeps identity/anchoring/conventions and
    the routing table. board.md: 34.7→9.7 KB; SKILL.md: 31.6→12.7 KB. No content was deleted —
    every recipe moved verbatim into its reference. Contract intact: command-surface suite green
    and README regions unchanged (docs-freshness gate). This weight was causing the very
    auto-compactions the run-ledger machinery exists to survive — paying the disease and the cure.
  • The brake hook stops taxing every tool call in every session (#572, part of epic #561).
    The PreToolUse hook fires on every Bash/Edit/Write in every session and paid a full pwsh
    interpreter spawn (measured ~1.3 s on the reference machine) just to discover that no
    brake marker exists — the case for every session except a launched autonomous run. At 100
    tool calls per session that was ~2 minutes of pure interpreter startup, paid by every user in
    every repo, and the tool had no way to see it (#568's blindness, costing #572's tax).
    Brake-PreCheck.cmd now answers the common case in ~0.25 s (5× cheaper): no marker in
    the working directory or three ancestors → exit 0 without ever starting pwsh. Fail direction
    unchanged: an armed worktree always reaches the real PowerShell guard with its fail-closed
    behavior intact, and stdin passes through untouched. Verified by 4 new tests, including an
    end-to-end deny through the shim over real stdin and a no-pwsh timing bound.
  • GitHub reads gain a cache, and the fail-closed wrapper gains a lint that stops the
    bleeding
    (#571, part of epic #561). Every gh invocation is a fresh process + TLS round trip
    and nothing was cached — a clean work cycle made ~50–65 calls, many re-reading facts that had
    not changed. Invoke-GhCached is a small cross-process file cache (in-memory caches die
    with each script's pwsh process) for read-only queries with an explicit TTL; failures are
    never cached, -Force busts, TTL 0 always fetches. The board's Status-schema read — the
    exact anti-pattern quoted in the wrapper's own header, still live — now goes through it
    (5-min TTL), and four more of the worst raw sites migrated to Invoke-Gh (issue blockers,
    claims, linked PRs, commit search: a 401 no longer reads as "no blockers/no claim/no PRs").
    A new lint test freezes the per-file raw-gh baseline (59 sites): counts may only go down,
    a new raw call names its file and fails, and stale allowances are themselves flagged.
    Verified by 7 new Pester tests (cache semantics + lint).
  • Evidence is written once, linked twice (#570, part of epic #561). The same evidence block
    used to be copied to three destinations (PR body, issue comment, evidence/<issue>.md) —
    three copies of one content, drifting independently, and the "INCOMPLETE → record → re-run"
    loop mostly existed to keep them in sync. Now evidence/<issue>.md is the single source of
    truth
    and the PR body and issue comment carry a link stub
    (Format-EvidenceLinkStub: marker + pass/fail summary + link to the file). The completion
    check tightened accordingly: a surface satisfies its requirement with the marker plus
    substance
    (the file reference or a full block — pre-#570 runs stay valid); a bare marker is
    a stamp anyone can leave, and now reads as missing. Verified by 7 new Pester tests.

Added

  • The definition of done scales to the diff (#569, part of epic #561). The contract's six
    gates all defaulted to true and the brief listed them flat, so a 10-line docs fix owed the
    same verify pass as a model migration. Get-ApplicableDodGates derives the owed gates from
    the changed paths — bpa/tmdlBreaking only when a semantic-model file changed,
    build/lint/tests only when something executable changed, ci always — and the
    work-class CLI prints which gates the current diff owes and which it does not. Fail direction:
    an unreadable diff owes every enabled gate, the derivation can only disable gates the
    contract enabled (never re-enable one it turned off), and an unrecognized custom gate is
    always owed. The autonomous brief now points the run at this instead of the flat list.
    Verified by 6 new Pester tests.
  • The tool can finally measure its own time (#568, part of epic #561). The telemetry
    subsystem measured everything except time: the transcript parser discarded the timestamp
    every line carries, the correlation window was "6 events" (4 seconds or 40 minutes,
    unknowable), the ledger's columns were events and bytes, started had minute granularity, and
    completed session rows were deleted — every run's wall-clock cost vanished at the moment it
    finished. Now: events keep their ISO timestamp; episodes carry ts + durationMs (unknown
    stays null, never a fabricated zero); per-session records and the field ledger gain
    durationMin; started is stamped to the second (the supervisor reads both formats); and a
    completed session row is archived to sessions-history.jsonl with ended and its outcome
    (pr-merged / cleaned / close-loop) before removal. Verified by 8 new Pester tests.
  • The visual work-class gate scopes to what the owner actually judges by looking (#567, part
    of epic #561). In a web app (a FabricApp) every change touches html/css/assets, so the
    classifier marked the whole project "visual" and the classification stopped carrying
    information — when the end-to-end gate reopens (#541), that would have routed 100% of web work
    to the human. The policy gains workClass.codeExceptions — globs subtracted from
    visualPatterns, declared per project in the contract (e.g. src/components/**/*.css for
    utility styles nobody screenshots), never guessed: the default list is empty and the
    fail-visual direction is unchanged out of the box. And the verdict now carries
    visualGroups — visual paths grouped by top-level section — so the owner approves "the
    pages section"
    once instead of 47 file rows. Verified by 6 new Pester tests.
  • The epic walker: /board expert auto -Epic <n> dispatches sub-issues wave by wave (#566,
    part of epic #561). Nothing advanced an epic before — Expert-Auto took one -Issue, so a plan
    with N sub-issues cost N human launches (Board-Plan even fetched the sub-issue list and threw
    it away). -Epic reads the epic's native sub-issues, classifies them (done / in-flight /
    blocked / ready) and launches one autonomous session per ready sub-issue with the
    contract's brake and budget, each briefed with the epic's enriched plan plus its own issue
    text. Idempotent by design: merge a wave's PRs and re-run the same command for the next
    wave; done and in-flight sub-issues are never re-dispatched, and a sub-issue whose PR state
    could not be read counts as in-flight — dispatching a possible duplicate session is the worse
    error, so the unknown fails closed. Verified by 7 new Pester tests on the wave classifier.
  • A stopped run now signals instead of sitting silent (#565, part of epic #561). A braked,
    out-of-budget, or stalled run was invisible from outside — the deny payload reached only the
    model, the supervisor's verdict existed only on a terminal nobody was required to watch, and
    the human learned about a stall by noticing the PR never appeared. Three surfaces fix that:
    every hook denial appends to a local denials.jsonl and posts one [abios-signal] issue
    comment per (kind, issue)
    — deduped by a marker file so a retrying agent cannot flood the
    issue (the brake marker now records the repo to make posting possible); the fleet supervisor
    gained -Post, publishing an [abios-stall] comment for each session past the threshold
    with no PR; and the session watch runs the supervisor automatically every 10 poll cycles,
    so stalls surface without a separate human command. All signaling is best-effort and can never
    change a verdict. Verified by 14 new Pester tests across the guard, supervisor and watch.
  • The expert run's time budget is now enforced, not advisory (#564, part of epic #561).
    Get-BudgetVerdict computed a handoff verdict that nothing ever called — the contract's
    120-minute budget existed only as a sentence in the brief, so a runaway run had no wall-clock
    limit at all. The budget now travels in the brake marker (budgetMinutes + armedAt) and the
    PreToolUse hook enforces it: past the budget, work commands are refused with an instructive
    message while the wrap-up stays open/board handoff -Save, committing and pushing WIP,
    closing the run ledger, and leaving a PR/issue comment. The brake always wins over the
    exemption (a push to main over budget is still a refused merge), and the budget fails
    open
    on a corrupt timestamp — it is a liveness limit, not a safety control, and must never
    brick ordinary work. Iteration counts stay advisory: a hook that sees single tool calls cannot
    count verify-loop iterations honestly. Verified by 24 new Pester tests (pure + hook end-to-end
    over real stdin); mutation-checked (enforcement disconnected → 3 tests red → restored).

Fixed

  • The review wait learns from silence, and any reviewer's answer counts (#563, part of epic
    #561). Two defects made every PR pay the full review timeout forever: the wait loop broke only
    on a review whose author was Copilot — a human or external review landing mid-poll kept it
    spinning — and the per-account cooldown (#367) armed only on an explicit "cannot review"
    answer, so a Copilot that was simply silent taught the gate nothing. Now the wait's arrival
    test is the same evidence rule as the verdict — any GitHub review or recorded external
    review
    ([abios-review] comment) bound to the current head commit ends it (stale evidence of
    earlier commits keeps waiting), the PR comments arrive in the same authoritative GraphQL read
    as the reviews (one call fewer per poll), and silence past the timeout arms a
    1-day cooldown (weaker evidence than an explicit refusal, which keeps its
    -CopilotCooldownDays default of 7). Verified by 10 new Pester tests; mutation-checked
    (author-only arrival and inverted silence check reintroduced → 5 tests red → restored).
  • The review gate's CI wait now has a ceiling, and the CI and review waits run concurrently
    (#562, part of epic #561). gh pr checks --watch was the only unbounded wait in the codebase:
    a queued or never-scheduled workflow hung the entire session indefinitely, with no signal. The
    gate now polls a structured checks snapshot in the same loop as the review wait — worst case is
    max(CI, review) instead of their sum — and the CI side expires at -CiTimeoutMinutes
    (default 25) into an explicit "checks still pending" BLOCK, never a silent hang and never
    a pass. An unreadable checks snapshot also blocks (fail closed) instead of being trusted from
    display text. Verified by 12 new Pester tests; mutation-checked by reintroducing both defects
    (cancel-counts-as-pass, sequential-OR exit) and watching 4 tests go red.