New doc (docs/architecture/workspace-concurrency.md) explains:
- why lockMode='none' is the default (cognitive tasks run in
parallel with no lock)
- what tasks.requires_filesystem means and how v52 backfills it
- the coding-role inference safety net during the v52 transition
- the per-task decision table the resolver implements
- why company_fallback was removed and what replaces it
- the type-enforced invariants on the resolution pair
- failure modes and how heartbeat/task-runner recover
Also adds /docs/architecture/*.md to the .gitignore allowlist so the
docs directory is tracked the same way /docs/*.md and
/docs/proposals/*.md already are.
Address T2 review asks from Karpathy (request_changes) + Jobs
(approve with minor asks) without touching published commits:
- scripts/codemod/README.md (new): records remove-appdatabase-passthrough.mjs
usage, the completeness proof for METHOD_TO_MODULE (typecheck-clean +
524 tests green at 118e3d8 = no orphaned call sites), and the sequential
revert recipe for C1 (3 -> 2 -> 1, not independent because 118e3d8
bundled vault simplification).
- CONTRIBUTING.md (new): pass-through ban + codemod usage + sequential
revert recipe + file-size ceiling + C6 wave cap. Whitelisted in
.gitignore alongside the existing doc exceptions.
- docs/proposals/C6.2-error-taxonomy-phase2.md: wave 2 scope boundary
names renderer-adjacent / api-server / browser / automation / skills
explicitly and excludes src/main/database/** + src/main/worker/runtime/**
(B2 / M10 high-churn zones). Wave 2 and wave 3 each capped at 30
catch sites so C6 cannot expand without bound.
- src/main/agent-runtime/artifact-projector.ts, runtime-bridge.ts:
N-scan TODOs now carry a death date ("TODO(M-followup by 2026-05-20)").
If the date passes without decomposition, a follow-up session MUST
revisit or justify the exception.
Karpathy T2 asks #1 and #2 (split part 3 / "independent revert" language)
are addressed in CONTRIBUTING + codemod README as documentation, not
history rewrites, because 118e3d8 is already published.
Typecheck clean. No structural code changes; pure docs + comment edits.
Each of these catches already logged via an accessible `logger` or `log`.
The migration wraps the thrown value through `classifyUnknownError(raw,
fallback)` and emits the log line with the structured `[code]` prefix.
Observable behaviour — return values, fail strings, DB writes — is
unchanged; the only addition is a machine-readable `code` on every log
line so future telemetry can bucket by category.
Sites migrated (11 files, 20 catch sites):
src/main/browser-action-service.ts (2) approval lookup, payload JSON
src/main/ipc-credentials-handlers.ts (4) save, remove, test lookup, test resolve
src/main/hire-hook.ts (3) getAgent, connector def, outer exception
src/main/checkpoint-engine.ts (2) onTaskStarted, onDocumentDrafted
src/main/document-lifecycle.ts (1) getRun while deriving taskId
src/main/skills/runtime.ts (2) builtin statSync, builtin readdir
src/main/worker/handlers/run-status-handler.ts (1) run not found during status
src/main/worker/handlers/run-log-handler.ts (1) lease renewal lookup
src/main/notification-service.ts (1) notification.close during destroy
src/main/updater-service.ts (1) autoUpdater.checkForUpdates
src/main/ipc-support.ts (2) ipc handler throw, emitEvent send
Why only 20 tonight: 248 catches exist in src/main/. Rewriting them all
in one commit is high-diff-risk (collides with Linus's in-flight
database/backup/migration work on database/*, connector-definitions-*,
message-* and scripts/*) and low-value (pure-library catches with no
accessible logger produce dead `void classified` constructs). The
taxonomy module is committed separately so the remaining 228 sites can
land incrementally via the C6.2 proposal.
All 11 files selected sit outside Linus's working set. Verified:
- pnpm typecheck clean
- pnpm test:unit 498 passed
Land the B5 harness skeleton: fixture loader (strict JSONL contract with
meta header + ordered turn records + assertions tail), replay-client that
swaps in for pi-ai's streamSimple by replaying recorded assistant turns,
structural assertion judge (tool_called / no_tool_called /
message_contains_substring / tool_call_arg_matches / trace_max_turns), an
eval-runner that sums the token ledger and produces an EvalResult, a
vitest driver (tests/eval/harness.test.ts) that discovers *.jsonl
fixtures, and a record-fixture CLI that writes fixture meta headers.
Hard contracts:
- No fallback: replay-client and fixture-loader throw on exhaustion /
malformed input rather than synthesizing defaults.
- No mock: types reuse @mariozechner/pi-ai AgentMessage / ToolCall
verbatim; shadow types are forbidden.
- No fake fixture data: tests/eval/fixtures/ ships only README +
.gitkeep. Real traces are recorded by the user against live providers.
package.json adds test:eval (vitest --passWithNoTests so an empty
fixture dir is green) and eval:record (CLI entrypoint). electron-builder
already excludes tests/ via its files: glob — fixtures never enter the
shipped .asar.
Three independently ugly bugs that all fed the same log flood:
* Electron's `WebFrameMain.send` writes every "Render frame was disposed"
to stderr via `console.error`. When the worker fans a second
agent-event past a reloading renderer, we produced thousands of
identical lines and stalled the main loop on stderr I/O. Added a
`mainFrame.detached` guard so the send is short-circuited before
Electron's own catch block fires, and installed a targeted filter in
`suppress-warnings.ts` (now actually imported from the entry point)
so the specific `console.error("Error sending from webFrameMain:"…)`
noise never reaches the terminal. Any other payload still surfaces.
* `dispatchAgentMessage` accepted the human-reader sentinel as a wake
target when an agent replied to `__board__` — `wakeAgentIfPossible`
then called `db.getAgent("__board__")`, threw, and the dispatcher
kept repeating on every send. Strip `HUMAN_SENDER_ID` at
`resolveMessageWakeTargets`, and keep a defense-in-depth guard in the
heartbeat so a stray caller (future automation fan-out) cannot
reintroduce the crash.
* Pi's skills loader rejects any SKILL.md whose frontmatter `name`
diverges from its parent directory. The four bundled `my-writing-style`
copies declared `name: writing-style-skill`, so every install emitted
a warning and dropped the skill. They also lived as orphan gitlinks
(mode 160000 without .gitmodules), and the global `*.md` gitignore
hid every other SKILL.md from version control. Fixed the frontmatter
to match the dir, converted the dangling submodules to plain trees,
and allowlisted `builtin-skills/**/*.md` so a fresh clone carries the
catalogue instead of silently losing it.
The artifact projector only registered write/edit. Everything bash
produced — build artifacts, files written via shell redirection,
git commits, generated code — stayed invisible to the desktop UI
and the audit layer. Filesystem truth and UI truth were
systematically out of sync.
This lifts bash onto the same filesystem artifact protocol as
write/edit. When a bash tool call succeeds in a git workspace, the
projector runs `git status --porcelain=v1 -z --untracked-files=all`
and upserts a work_products row for every modified/untracked entry,
reusing the existing `(companyId, provider='filesystem', externalId)`
identity so repeated edits never duplicate. Non-git workspaces fall
through with an explicit "not_a_git_repo" skip reason — there is no
authoritative baseline there to diff against, and silently
half-registering would be worse than skipping.
A 50-file cap per invocation bounds the scan so a runaway rebuild
cannot flood the table; anything past the cap is logged as a skip.
Rename entries (emitted as `R new\0old`) advance the iterator past
the old-path entry so deletions of the rename source are not
double-registered.
The projector now always returns an array so `write`/`edit` (one
path) and `bash` (many paths) share one shape, and the hook handler
can log uniform outcomes.
.gitignore gains `AgentCompany-Rust/` — the separate Rust rewrite
tree lives in the same repo root but is not part of the desktop app
build and should not drift into feature commits.
Remove files that should never have been tracked:
- pi-mono-reference/: 688-file copy of an upstream monorepo, zero imports,
used only as offline human reference
- build/electron-builder.runtime.json: environment-specific build artifact
with hardcoded local paths
- state/cancel-signal-state.json: runtime state written by the app
- prompt.txt, logo.svg (root): exact duplicates of the canonical copies
under .planning/archive/ and assets/
Add state/ to .gitignore so future runtime state stays out.
- Add deliverable review gate: pause workflow when agents produce important
documents (PRDs, design docs, etc.) for human review before continuing
- Add company-level reviewDeliverables setting with onboarding card selector
- Block agent heartbeats when deliverable review is pending (agent + CEO)
- Trigger review at document creation time in API server for reliability
- Fix autoApproveHires vs reviewDeliverables conflict in approval-policy
- Fix app quit: force-exit on all platforms (macOS Dock, Windows tray, Linux)
- Add multi-platform CI/CD pipeline (macOS arm64/x64, Windows x64/arm64,
Linux x64/arm64 with deb/AppImage/rpm/snap) with code signing support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>