The two real goal tasks from July 9 each got exactly one session: the run
completed, the agent never moved the task off backlog, and the heartbeat's
"has a completed run" exclusion locked them out forever. The agents could
not have closed them anyway — update_task_status requires a task_id the
prompt never carried.
The progress contract, made explicit end to end:
- The task prompt now carries "Task id: ..." and tells the agent exactly how
to end a session: done via update_task_status when the work is complete,
in_progress with a note on what remains when it is not.
- The heartbeat re-enqueues any open task with no run in flight — a
completed run that left its task open earns a follow-up session, with the
prompt stating how many prior sessions were spent so the agent continues
instead of redoing work. The retry budget (3 non-transient runs) still
bounds every task; a task that spends it parks with one log line naming it.
- Scripted agents in the integration suites now close their tasks the way
the contract expects real ones to.
The July 9 run died at 07:22 when the GPT account hit its plan-side usage
limit: every later request failed with "The usage limit has been reached",
each failure counted against the 3-attempt retry budget, and 31 tasks were
abandoned for good while the worker kept hammering 12 doomed requests a
minute until the queue drained.
Root fix, three parts sharing one classifier:
- isTransientProviderFailure() in provider-runtime-domain recognises
provider-side verdicts (usage limit, rate limit, 429, overloaded) plus the
internal monthly-quota message; matching is against recorded
task_runs.error_message rows, so historical damage heals retroactively.
- enqueueHeartbeatRuns computes the retry budget with that predicate instead
of an exact match on the internal quota string alone.
- A transient failure parks the provider for a 10-minute cooldown: the
heartbeat stops enqueuing its tasks and the worker leaves its queued runs
unclaimed, then a single probe run re-arms or clears the park. Transitions
are logged once each way.
Tests: predicate unit tests, budget-exemption regression on the raw provider
message, cooldown park/resume with an injected clock, claim exclusion.
The whole app turned to molasses because snapshot.load() shipped the
entire skill_runtime_snapshots table - unbounded, compiled_prompt
included - over IPC on every refresh. A real profile carried 142 rows
totaling 144MB (1.1MB each from the pre-cap CEO compile-all era), so
every coalesced refresh serialized ~144MB of JSON. The forced-humanizer
rework made it worse by adding a row per run for every agent.
- Drop skillRuntimeSnapshots, skillRevisions, skillTests, and
skillBindings from the renderer snapshot: no renderer surface reads
them; skill internals are main-process state. Only the lightweight
skillDefinitions catalogue still ships.
- Project provider_requests without raw_response_json (4.1MB across the
newest 100 rows; RunsPage only reads responseText)
- Migration 051 purges the bug-era compiled prompts from existing DBs
- resolveRuntimeSnapshot is now idempotent per run: a run re-queued by
startup recovery reuses its frozen snapshot instead of dying on the
uniqueness guard and burning real retry budget
- Regression test pins the contract: a 2MB database row must not add
bytes to the snapshot (payload asserted under 400KB)
Skills no longer come from the user's ~/.agents library - most users have
none, and the CEO branch that compiled the entire catalogue into its
prompt overflowed the model context (1.1MB) and failed every CEO run.
- Bundle "built-in skills/" with the app (extraResources) and stage it
at startup into <profile>/builtin-skills as one dir-per-skill copy -
both bundle shapes (SKILL.md dirs and flat category .md files)
normalize into the same importable layout
- Import the staged catalogue per org at startup and inside onboarding,
so the CEO's first run already carries it
- Forced house style: resolveRuntimeSnapshot unions the humanizer skill
into every agent's runtime (CEO included) as a resolution rule - no
binding row to forget, delete, or bypass; revoke_skill refuses it
- CEO staffs hires with skills: hire_agent accepts a skills array,
grant_skill/revoke_skill adjust later, list_skills now shows
descriptions from skill frontmatter
- Kill the c_suite compile-all branch; everyone resolves bindings-only,
capped at 200k chars with omissions named in the prompt
- Remove the Skills settings page and its import/sync/test IPC surface;
drop ~/.agents discovery and the AGENTCOMPANY_NEW_SKILLS_ROOT seam in
favor of AGENTCOMPANY_NEW_BUILTIN_SKILLS_DIR
- Tests: staging/import/forced-resolution/cap units, real-bundle CI
check, e2e asserts the tab is gone and the catalogue self-imports
The heartbeat scheduler enqueued runs even while a provider's monthly
usage window was full, so every task burned its 3-attempt retry budget
on instant quota failures within seconds and stayed dead forever -
raising the limit (or restarting) never revived anything.
- Park tasks (skip enqueue) while their provider is at its
monthly_request_limit instead of creating runs doomed to fail
- Exclude quota-blocked failures from the 3-attempt retry budget,
retroactively reviving tasks abandoned by past filled windows
- Share the gate message as PROVIDER_MONTHLY_LIMIT_MESSAGE and log
quota-block transitions once per state change for diagnosability
- Ignore landing/ (self-contained Next.js sub-project) and
built-in skills/ (prompt content) in eslint so verify:ci lints only
application code
The legacy application - its src, tests, landing site, promo assets, docs,
release tooling, CI workflows, and root configs - leaves the tree; it stays
reachable in history. Everything under AgentCompany-New moves up unchanged:
same package name, so the production userData/database path is untouched,
and e2e/build scripts already resolved paths relative to the project. The
developer cred.txt stays gitignored at its new location. Full verify:ci
passes from the new root.