Skip to content

release: Task Orchestrator 4.0.0 - #625

Merged
jpicklyk merged 170 commits into
mainfrom
land/4.0.0
Oct 22, 2026
Merged

jpicklyk merged 170 commits into
mainfrom
land/4.0.0

Conversation

@jpicklyk

Copy link
Copy Markdown
Owner

Lands release/4.0 (75df163d) on main by a merge commit. 3.x maintenance continues on release/3.x (cut from main at 5322daf8). This PR retires the pr-base-guard workflow, whose only job was to keep 4.0 work off main.

Full release notes: CHANGELOG.md section ## [4.0.0] (158 KB; the GitHub body limit is 64 KB). The upgrade section follows.

Upgrading from 3.x

4.0 changes the wire contract (tools, request and response shapes, REST routes), the database schema (migrations V18 to V37 plus startup data steps) and the config format. Read this before the first 4.0 start on an existing volume.

  • Back up the volume before the first 4.0 start. Migrations V22 (dependency rewrite), V24 (plan_documents into documents), V25 (project_config into config_versions), V26 (work_items recreation) and V29 (role_transitions into events, then dropped) and the type-backfill, traits-from-properties and pin-backfill data steps rewrite data one way. The server also writes a verified <db>.pre-4.0.<yyyyMMddTHHmmssZ>.bak beside the database before the first migration (UPGRADE_BACKUP, default on), but it sits on the same volume, so keep a copy of the whole volume too. The backup is the supported rollback, and a 3.x binary cannot open a V26 or later database.
  • Stop every 3.x process first, stdio sessions included. A 3.x binary does not fail closed on a newer database, and a 3.x writer that inserts an IS_BLOCKED_BY row fails the V22 CHECK.
  • The first start is slower. It runs the data steps (type-backfill, traits-from-properties, config-canonicalize, pin-backfill) and the item FTS rebuild of V26 before the readiness marker is written; a failed step fails startup as DATA_STEPS and the container reports unhealthy. Raise the Docker --start-period on a large database.
  • The migration-lock wait is fixed at 300 s. A second boot waits that long for the first to finish its UPGRADE_BACKUP copy and migration; a first boot of a very large database can outlast it, and the waiter then fails startup closed as SCHEMA_UPDATE and takes no copy. There is no environment key for the wait; start the instances of a shared volume one at a time.
  • The mounted global config boots with warnings. The file is imported at every boot and then served from the database. The 3.x top-level keys schema_resolution, orchestration, retrospective and actor_attribution are accepted at boot as stored warnings (returned as configWarnings by GET /api/v1/info); a push of the same text is rejected. Move plugin settings under extensions.claude-code. Any other strict error, a file over 128 KiB, or a project.rootId that is not a depth-0 item of this database fails startup as DATA_STEPS.
  • Untyped items get a type. type is required in 4.0. type-backfill gives every item whose type is NULL or blank the schema key the 3.x tag step resolved it to, or untyped when none matched, and traits-from-properties moves properties.traits into the typed traits list.
  • In-flight items keep their 3.x schema; new items follow the new precedence. pin-backfill pins every item at the first start to the base schema 3.x gave it, so no gate moves for existing items. An in-flight 3.x parent already in review no longer auto-completes when its last child completes: it needs an explicit advance, and the cascade entry reports reason review-pending. New items resolve by type only: per-root exact type, server exact type, per-root default, server default. Tags no longer select a schema, a server exact type now beats a per-root default, a root that used schema_resolution: isolated now sees server entries, and duplicate trait note keys resolve item-first with required OR'd.
  • Stored data moves. Each project_config row becomes version 1 of its root's config (V25, ids preserved); the 3.x transition trail moves into events as backfilled item.transitioned rows (V29); plan_documents become documents (V24; a CRLF body keeps its 3.x hash until it is rewritten); IS_BLOCKED_BY rows are rewritten as BLOCKS (V22); every timestamp is normalized to UTC text (V18).
  • Notes written before 4.0 have no owner. 4.0 enforces note ownership (see Added: note ownership), and a 3.x actor id was declared by the caller, so none is trusted as an owner. V34 adds notes.owner_id, NULL for every existing note, and changes no other column: each note keeps its 3.x actor and verification. The first 4.0 upsert of such a note, by any caller allowed to write the item (in its scope, holding write-notes, and the claim holder while the item is claimed), makes that caller the owner and records an audit note.ownership_transferred with from: null and the 3.x actor id as legacyActor; after that, another principal is refused note_owned_by_other unless the key is shared: true or an admin passes takeover: true. In-flight items need no takeover. Until its first 4.0 upsert, such a note can be deleted by any caller allowed to write the item, as in 3.x; this includes notes a 3.x REST token wrote.
  • Saved SSE cursors re-fetch once. Event ids are now the durable events seq, above 10^12; a Last-Event-ID saved before the upgrade is answered with sync.lost (unknown_event_id).
  • Short ids need 8 hex characters. Every id field takes a full UUID or 8 to 32 hex characters; 3.x accepted shorter prefixes.
  • Environment. USE_FLYWAY is removed and ignored (one WARN if still set) and API_WARN_ON_CLAIMED_ADVANCE is removed. SCHEMA_MODE, FTS_INTEGRITY, MCP_TOOL_PROFILE, MCP_ALLOW_UNAUTHENTICATED, the four retention settings (RETENTION_CALL_LOG_DAYS, RETENTION_EVENTS_DAYS, RETENTION_AUDIT_EVENTS_DAYS and RETENTION_LEASE_HISTORY_DAYS) and API_JWKS_MAX_STALE_SECONDS are new, and DATABASE_BUSY_TIMEOUT_MS now governs only the startup paths. The default CORS_ALLOWED_HEADERS gains X-Client-Host and the default CORS_EXPOSE_HEADERS gains X-Req-Id; a deployment that sets either explicitly keeps its own list.
  • Clients and the plugin. Every 3.x tool call shape and every 3.x REST resource route is gone (see Removed), and every /api/v1 body is the envelope. The 3.x plugin does not work against a 4.0 server: its hooks fail open, so nothing is blocked, but nothing syncs or gates either. Install the 4.0 plugin (see ### Plugin); to stay on a 3.x server, pin the plugin to the release/3.x ref.
  • Key fetches refuse non-public addresses. Every JWKS and OIDC discovery fetch now passes an egress guard: a key source that resolves to a loopback, link-local, private (RFC 1918), CGNAT, unique-local or unspecified address fails, and verification reports unavailable naming the refused address (REST fails the request closed). A private identity provider or a corporate JWKS on 10.0.0.0/8 stops working on upgrade; loopback is admitted only with API_JWKS_ALLOW_INSECURE_URL=true (REST), and other private ranges have no opt-in in 4.0.
  • /mcp over HTTP authenticates, and actor is gone. With the REST API on in a credential mode (API_AUTH_MODE=bearer or jwks), every /mcp, /mcp/knowledge and /mcp/director request needs the same Authorization: Bearer credential as /api/v1; without one it answers 401 unauthenticated (the error envelope, never JSON-RPC). MCP_TRANSPORT=http with the API disabled now fails startup (MCP_AUTH) unless MCP_ALLOW_UNAUTHENTICATED=true is set on purpose. Clients stop sending actor: the caller is the transport principal, and a body actor is unknown_parameter. Audit ids written in REST unauthenticated mode change from api:local-unauth to local.
  • Document writes need write-docs. Every docs_write op now requires the new write-docs capability, and write-config no longer grants any of them. A token that only had write-config (for example a config-sync token that also pushes plans or rules) is refused with forbidden until write-docs is added to its capabilities list.
  • Principal root scope is roots-only. A scope anchor (scope.root_ids) admits exactly the items stamped with that root, so a mid-tree anchor admits nothing. A REST bearer token whose scope.root_ids names an existing non-root fails startup, naming the token; a JWT anchored on a non-root resolves to no principal (401). Creating a root, or moving an item to a root, needs an unrestricted scope or ADMIN.
  • DEGRADED_MODE_POLICY is removed and fails startup. Verification fails closed, so there is no policy to choose: unset DEGRADED_MODE_POLICY (any value) before the first 4.0 start, and delete actor_authentication.degraded_mode_policy together with the rest of the actor_authentication block (next entry). A leftover variable stops the server with an error naming it, before the readiness marker is written, because ignoring a security setting an operator believes is in force would be worse than refusing to start. A deployment that relied on accept-cached or accept-self-reported to keep working through a key-set outage now gets 503 unavailable instead (see Changed). Wire clients that read a note's attribution read verification.proofStatus, not verification.status (see Changed).
  • actor_authentication is removed, and a leftover block fails startup. No request carries an actor or an actor.proof any more, so the verifier the block configured was consulted by no call, and require_sub_match, jti_replay_protection, DID trust (did_allowlist, did_pattern) and the loose-kid match had no effect. A global config with an actor_authentication: key, whatever its value (a mapping, {}, a scalar or a bare null), now stops the server with an error naming it, before the readiness marker is written; the legacy auditing: key still fails startup too. A per-root push carrying it is rejected at /actor_authentication as an unknown top-level section. REST break: GET /api/v1/info no longer returns actorAuthenticationEnabled; clients that read it must stop. Solo stdio: a server with no AGENT_CONFIG_DIR reads the workspace .taskorchestrator/config.yaml as its global file, so a hook-only actor_authentication: {enabled: true} block there now fails startup too; delete it. The plugin's attribution hook is removed (the server principal is the identity), so there is no plugin-side attribution requirement to configure. A call's identity comes from API_AUTH_MODE alone: the credential's principal (api:<id>), or the single local principal for stdio, /mcp in a local mode and API_AUTH_MODE=none. Move any actor-proof JWKS setup to API_AUTH_MODE=jwks and the API_JWKS_* variables; delete the block before the first 4.0 start.
  • The first start folds and prunes. The retention pruner runs right after startup. It folds each completed UTC day of call_log into usage_daily, then deletes call_log rows older than 90 days (folded days only), activity-class events older than 180 days and closed lease intervals older than 365 days; audit events are kept. To keep any of that data, set the matching RETENTION_CALL_LOG_DAYS, RETENTION_EVENTS_DAYS or RETENTION_LEASE_HISTORY_DAYS to 0 before the first start. The database file does not shrink without an offline VACUUM (see Data Retention in current/docs/fleet-deployment.md).
  • Credentials without a kind are service. A token-file entry with no kind and a JWT with no to_kind load as kind service (a self-issued did: token is agent). Answering a decision whose class is human-only needs a credential of kind human (kind: human in the token file, to_kind: human in a JWT).
  • Stdio and local sessions are one principal. Every stdio session, /mcp in a local mode and API_AUTH_MODE=none run as the single local principal, which owns the notes and claims they write, so separate sessions share them unless they hold seat tokens (see run seats under Added). Separating them otherwise takes credentials.

🤖 Generated with Claude Code

jpicklyk and others added 30 commits October 7, 2026 14:01
* fix(advance): suppress terminal cascades for BLOCKED parents and unmet dependencies [063459ec] (#433)

* fix(advance): suppress terminal cascades for BLOCKED parents and unmet dependencies [063459ec]

A terminal cascade applied the parent's TERMINAL transition without validation, so a held or dependency-gated parent was silently completed when its last child finished. AdvanceService now checks the role and blocking dependencies before the note gate and reports roleBlocked/dependencyBlocked on the cascade event.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(advance): cover cascade BLOCKED and dependency guards [063459ec]

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(complete-tree): stub parent dependency lookups for the cascade re-validation; fix CascadeEventDto KDoc [063459ec]

The F-003 terminal cascade re-validates the parent's blocking dependencies, so the strict depRepo mocks in CompleteTreeToolCascadeOrderTest S3/S12 need parent-id stubs (planned existingTestEdits). CascadeEventDto KDoc now lists every applied=false reason (review observation 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix: audit 2026-10-07 medium findings (14 items) (#435)

* fix(hooks): yaml-lite matches column-0 headers and first-level keys only [d07b151f]

A nested indented project:/mode: key could shadow the real top-level section or sibling key; headers now match on the raw line at column 0, scalar is restricted to the block's first-level indent, inlineScalar to brace depth 0, with a hook/mod parity test.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(startup): clear stale readiness marker before DB init [3df042a7]

After SIGKILL/OOM the marker survives in the container layer, so a restarted container reported healthy during migration or after a failed start. Clear it first in run() and fail closed as READINESS_MARKER if it cannot be removed.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(api): skip ETag/304 on GET /items/{id} when include is requested [f5691011]

Notes, deps and children are not versioned by item.modifiedAt, so an item-only validator served stale 304s; mirrors /gate and /schema.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(config): guard live config files and reword pinned golden fixtures [53ba2bd6]

Live floor and dogfood config files had no parse test and the golden fixtures were described as verbatim copies while drifting; add LiveConfigFilesParseTest and document the fixtures as deliberately pinned seat-less snapshots.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(plugin-hooks): shape-faithful retro/config-sync fixtures, ralph worktree helpers in lib with argv git [943f729d]

retro-trigger read the bare tool_response and missed structuredContent; config-sync emitted a SessionStart hookEventName for FileChanged; ralph-loop shell-interpolated model-supplied itemId and baseRef. Move worktree naming/rename/cleanup into ralph-lib with an injected argv git runner and add tests.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(api): GET /items/{id}/transitions honors the page parameter [10e26852]

The route computed pp.offset but never passed it to the repository, so every page re-served page 1. Add offset to findByItemId and a deterministic id tiebreaker so offset paging is stable across equal timestamps.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(db): fail fast on Direct-mode databases instead of a failing Flyway baseline [ef8355ae]

Direct-mode DBs have no upgrade path: Flyway now refuses a table-bearing DB with no history (actionable error), clean is disabled, baselineOnMigrate removed, and Direct mode fails when declared columns are missing.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(api): document real 401/403 auth error codes and error_description envelope [0fba5c6e]

api-rest.md and openapi.yaml listed a phantom 401 unauthenticated code and claimed every error uses {error, message}; the auth plugins and SSE pre-flight emit invalid_request/invalid_token/insufficient_scope/validation_error with error_description. Docs now match the wire; add AuthErrorDto and oneOf for 403.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(config): drop unconsumed actor_authentication.enabled from the server model and document it as hook-only [bd9d7cc0]

The server parsed enabled but nothing read it, while the plugin hook reads it from the client-side config; docs sent operators to the global file where the hook never looks. Remove the dead field and state which reader uses which file.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(auth): static JWKS actor verifier requires audience and issuer [ef5adada]

Without them any token signed by a key in the JWKS was accepted. Static mode now fails startup when audience (or issuer, absent oidc_discovery) is missing, and an OIDC config with no resolved issuer rejects at runtime. DID mode unchanged.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(api): reject unparsable filter values with 400 validation_error instead of widening [4c498cbe]

GET /items, /items/{id}/tree, /transitions, /search and /notes/search dropped unparsable parentId/rootId/ancestorId/role/priority/claimStatus/since/depth/timestamp values, returning the unfiltered set (the whole database for an admin token). Shared QueryParams parsers now 400 any supplied-but-invalid value before scope checks; blank stays absent. Docs and OpenAPI updated, phantom CRITICAL/BACKLOG priorities removed.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(api): GET /transitions scope filter fails closed on lookup error [38770b2b]

The root-scope block returned rows unfiltered when findByIds errored. Replace both scope blocks with the shared allowedItemIdsForScope helper (one batched ancestor lookup, denies on error), also retiring the per-item ancestor N+1.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(items): route manage_items and POST/PATCH /items through one WorkItemPlacementService [ae3a9764]

The create and reparent-with-depth-cascade pipeline existed twice and had drifted: the MCP cycle guard failed open on an ancestor-lookup error while REST failed closed. One application-layer service now owns the guard and the single-transaction placement write; MCP update now fails closed too. Removes the fail-open validateAndComputeDepth.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(api): GET /transitions scope filter fails closed on lookup error [38770b2b]

Regression and guard tests for root/tag scope filtering when findByIds or findAncestorChains fail; red on b688ea0 for S1 and S6.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(items): chunk subtree id lists and delete a subtree in bulk by traversal level [ac4b15bb]

findDescendants/findByIds/deleteAll bound one variable per id, and recursive delete issued ~3 statements per descendant ordered by the stored depth column, which can be stale. IN lists now chunk at SQL_IN_CHUNK_SIZE inside one transaction; recursive delete releases leases with one releaseAllForItems call and deletes level by level (levels derived from parentId links, deepest first) via deleteAll, all-or-nothing as before. SSE ITEM_DELETED order changes from strictly deepest-first per row to level by level, deepest level first; one event per row is preserved. Reparent depth restamp stays per-row (documented follow-up).

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(api): assert transitions paging on serialized fields [10e26852]

The per-item transitions route does not serialize RoleTransition.summary, so the paging tests now count rows by trigger and identify them by occurredAt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: fix verifier examples and GET /items/{id} responses [audit-a1]

Covers ef5adada (F-011) and f5691011 (F-014).

Seat: implementer-a1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs: accuracy fixes from audit review [audit-a1]

Covers 38770b2b, 3df042a7, bd9d7cc0, ef5adada, 4c498cbe, 0fba5c6e, ef8355ae, ac4b15bb.

Seat: implementer-a1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix: audit review hardening and test gaps [audit-a1]

Covers ef5adada (blank audience/issuer rejected), ef8355ae, ac4b15bb, 4c498cbe.

Seat: implementer-a1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(changelog): audit-a-2026-10-07 entries [audit-a1]

Covers 38770b2b, 10e26852, 4c498cbe, f5691011, ae3a9764, ac4b15bb, ef8355ae, 3df042a7, bd9d7cc0, ef5adada, d07b151f, 943f729d.

Seat: implementer-a1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(changelog): fold the stdio EOF bullet into the Unreleased Fixed section [audit-a1]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(api-reference): verifier example uses exactly one key source [ef5adada]

The field-reference example set oidc_discovery, jwks_uri and jwks_path together, which the exactly-one-source rule rejects; alternatives are now commented and required keys marked (re-review-a1 observation).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(auth): static JWKS verifier warns instead of failing on missing audience/issuer [ef5adada]

User decision: non-breaking for existing configs (AgentLair jwks_path). Restores b688ea0 parsing and verification; adds startup and one-time runtime WARNs.

Seat: implementer-a2

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… [7eb28917] (#437)

* feat(errors): domain error catalog, Outcome and BatchResult types [7eb28917]

Adds domain/error: ErrorCode (27 codes), ErrorDetail, DomainError, ErrorFixTemplates, Outcome, BatchResult. ErrorKind moves from domain/model/ToolError.kt; imports updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(errors): repoint ErrorKind imports after relocation to domain/error [7eb28917]

Orchestrator construction-only fixture repair (import lines only, no assertion changes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(errors): independent catalog, redaction, DomainError and BatchResult tests [7eb28917]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(errors): align ErrorKind KDoc with the envelope retry semantics [7eb28917]

Review observation A3: the relocated KDoc kept the 3.x meanings (transient=backoff, shedding=explicit delay), which envelope section 2 inverts. KDoc only; no code change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…artup integrity [ba942a00] (#438)

* feat(db)!: Flyway as the only schema path; migration hardening and startup integrity [ba942a00]

Delete Direct mode and USE_FLYWAY, move migrations to db/migration/sqlite, fail closed on future versions, add SCHEMA_MODE, OS migration lock with BUSY retry, Direct-DB baseline-or-refuse, LIFO shutdown, and FTS/trigger startup integrity checks. Docs and skills updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): construction-only fixture repair after Flyway-only switch [ba942a00]

Orchestrator-delegated fixture repair: test bridge restored under src/test, non-null coordinator args, sqlite/ resource paths. No assertion changes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): independent Flyway-only, schema mode, lock, integrity and LIFO shutdown tests [ba942a00]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(db): exclude Flyway bookkeeping objects from the Direct-mode V17 shape compare [ba942a00]

Arbitration case 1 on S3: the reference DB's flyway_schema_history table and index can never exist in a history-less candidate.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(db): review fix-ups for Flyway-only (migration-review recipe, VACUUM wording, SCHEMA_MODE caveats, breaking-change flag) [ba942a00]

Observations 1-5, 9, 11, 17, 18: trigger counts and rowid-preserving INSERT, VACUUM/FTS wording, V9 correction, SCHEMA_MODE=validate and invalid-mode caveats, real version log after no-op migrate, ahead-of-binary remedy, breaking CHANGELOG flag.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ncy graph types [7d7a3a7b] (#439)

* feat(domain): declarative transition table, ordered gates, TransitionPolicy and dependency graph types [7d7a3a7b]

Adds domain/lifecycle (Trigger families, data-driven TransitionTable, TransitionSnapshot, ordered StandardGates, pure TransitionPolicy with Decision/FollowUp) and domain/graph (BlockingEdge, DependencyEdges.normalize, BlockerEvaluator, CycleDetector, TopoOrder).
Pure additive domain code; no wiring. Rejections use the P1 DomainError catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(domain): independent transition table, policy and dependency graph tests [7d7a3a7b]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(domain): close review gaps - cascade dependency gate, cascade leases, SCHEMA_FREE, warrant-before-hold [7d7a3a7b]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…upgrade harness [f2df53b7] (#440)

* test(db): SQLite test fixture, all-table schema parity and populated upgrade harness [f2df53b7]

SqliteTestDatabase (Flyway template per JVM, production DatabaseManager, JUnit extension), SchemaParityTest over every table with a committed sqlite_master snapshot, and an upgrade harness with per-migration seeds plus a synthetic golden V17 database.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): seed every column, scoped seed transforms, real-startup golden check and review fixes [f2df53b7]

B1: BaselineDataset gives every column a distinct non-default value (type, properties, metadata, summary, proof sha256/claims, verification_reason, status labels, lease budgets); BaselineColumnCoverageTest guards the latest schema with a reasoned exemption list; raw actor_proof only seeded below V17 so the regenerated golden matches 3.16.

O1: per-seed expected(table,row) transform verified exactly and scoped to steps whose chain includes the migration. O2: probe guard gated on pending versions > 17. O3: golden startup test runs DatabaseManager.initialize+updateSchema with compaction and asserts FTS rowid resolution. O4: sidecar asserts made meaningful, leak-fail path tested. O5: tautology replaced by cross-test perClass persistence. O6: schema snapshot split per table; trigger message fixed. O11: regenerate-to-temp sha test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): distinguishable seed columns, pairwise guard, rowid gap for compaction check [f2df53b7]

N1: created_at/modified_at/role_changed_at, notes and plan_documents created_at/modified_at, and resource_leases original_acquired_at/acquired_at now carry distinct literals; new pairwise guard in BaselineColumnCoverageTest (enumerates the migrated schema, empty reasoned exemption list, non-vacuity self-test). N2: UpgradeHarness KDoc describes expected() transforms compared exactly. N3: golden has a rowid gap (sacrificial rows deleted); the real-startup test wipes the FTS indexes and asserts startup restores MATCH-to-row mapping.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): correct the golden rowid-gap comment and FTS restore messages [f2df53b7]

Review r3 observation R3-O1: VACUUM keeps implicit rowids on the bundled SQLite, and the FTS restore after startup can come from the integrity check or compaction. Comment and assertion messages only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…89265] (#441)

* test(tools): move config/service batch onto SqliteTestDatabase [55d89265]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(tools): move tools/compound/config/dependency batch onto SqliteTestDatabase [55d89265]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(tools): move tools/items+notes batch (adds FTS S4-S6) onto SqliteTestDatabase [55d89265]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(tools): move tools/workflow batch onto SqliteTestDatabase [55d89265]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… [87a1b5ef] (#442)

* test(db): fixture perClass guard, CycleTriggers and UpgradeHarness.copyAt [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move split-executor migration tests (V9-V17, Fts5) onto UpgradeHarness copyAt [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move H2 repository tests (B2) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move BaseRepositoryTest subclasses and H2 config tests (B3) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move SQLiteRepositoryTestBase subclasses (B4) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move FTS/hand-DDL repository tests (B5) onto SqliteTestDatabase; port H2 cycle scenarios [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…y error translation [P5a] (#443)

* test(db): fixture perClass guard, CycleTriggers and UpgradeHarness.copyAt [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move split-executor migration tests (V9-V17, Fts5) onto UpgradeHarness copyAt [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move H2 repository tests (B2) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move BaseRepositoryTest subclasses and H2 config tests (B3) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move SQLiteRepositoryTestBase subclasses (B4) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move FTS/hand-DDL repository tests (B5) onto SqliteTestDatabase; port H2 cycle scenarios [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(errors): boundary translation of persistence faults to the error catalog [9343ad8d]

PersistenceFaults classifies SQLite result codes along the cause chain (BUSY/LOCKED, pool checkout timeout, unique/primary-key, foreign-key, readonly) and maps them to unavailable, duplicate, not_found and internal, keeping the innermost SQL message. ErrorDetail.NotFound.id and ErrorDetail.Duplicate.existingId become nullable: they may be omitted only for a raw constraint translation at the unit-of-work boundary.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(db): UnitOfWork port, ambient unit and writer/reader pools [9343ad8d]

Adds the UnitOfWork port (write/read scopes, afterCommit/afterRollback hooks, UnitElement), Clock/SystemClock and the cancellation helpers. DatabaseManager now builds a size-1 IMMEDIATE writer pool and a query_only reader pool over HikariCP 7.1.0 (per-Database isolation, defaultMaxAttempts=1, in-memory URLs refused); UnitRunner owns the writer Mutex, whole-unit BUSY retry with a 10 s deadline, fresh ConfigSession per attempt, once-only hooks, cancellation cleanup and eviction of a poisoned connection. All 80 transaction sites move to readTx/writeTx, RepositoryProvider moves to application.port without database(), DependencyWriteRoutes uses unitOfWork.write, PerRootConfigService never serves last-known-good inside a unit, and a failed or repair-only start closes the pools.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(db): connection pools, DATABASE_MAX_CONNECTIONS and busy-timeout scope [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(db): cancellation-safe cleanup for implicit reads and writes outside a unit [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): independent unit-of-work, pool and translation tests [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(arch): context-safety, transaction-opener and cancellation guards with baselines [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(perf): advance latency benchmark gated by TO_PERF [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): close red-proof gaps - writer queueing past checkout window, bounded waits, FQN opener detection [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* style(test): ktlint formatting for the advance latency benchmark [9343ad8d]

Formatting only (ktlintFormat); no logic change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(db): bound writer lock wait by the unit deadline, narrow pool eviction, shim hook contract [9343ad8d]

Also documents the store Result.Error caveat on UnitOfWork and reframes the DATABASE_BUSY_TIMEOUT_MS recommendations as startup-only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): bounded writer wait, no eviction on constraint faults, production wiring, shim hooks, opener gaps [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(db): never leak the writer Mutex when the bounded wait times out; document the deadline overrun [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): writer lock is never leaked when a bounded wait times out at release [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): make the writer-lock leak regression reliable [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): writer lock is freed when a waiting write unit is cancelled at the grant [9343ad8d]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(db): release the writer Mutex when the caller is cancelled after the grant [9343ad8d]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(db): release the writer Mutex on post-grant cancellation without a catch-all [9343ad8d]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… and production H2 branches [P4c] (#444)

* test(db): fixture perClass guard, CycleTriggers and UpgradeHarness.copyAt [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move split-executor migration tests (V9-V17, Fts5) onto UpgradeHarness copyAt [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move H2 repository tests (B2) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move BaseRepositoryTest subclasses and H2 config tests (B3) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move SQLiteRepositoryTestBase subclasses (B4) onto SqliteTestDatabase [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move FTS/hand-DDL repository tests (B5) onto SqliteTestDatabase; port H2 cycle scenarios [87a1b5ef]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* build(test): run test classes in 4 parallel forks to hold suite runtime [facdac00]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(api): complete shared MockRepositoryProvider with a reflection completeness test [facdac00]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(api): move REST route and event tests onto the SQLite fixture [facdac00]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(api): move MCP, auth and fixture-base tests onto the SQLite fixture; delete H2 test bases [facdac00]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(db): remove H2-only branches from production repositories [facdac00]

Drops the h2 dependency, the production H2Dialect branches and H2 docs; resets test heap to 1g; archives the v2 workflow testing plan.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(db): move the P5a composition and custom-database tests off H2 [facdac00]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test: completeness guard follows the 8-accessor RepositoryProvider after P5a [facdac00]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(test): correct FTS pagination test docs after the H2 removal [facdac00]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…erialTest task [facdac00] (#445)

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…e-unit writes fail [P5b] (#446)

* refactor(db): unit-of-work plumbing, poisoned units, outside-unit policy [c01d2e90]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(db): every write site inside a unit; remove inTransaction and marker exceptions [c01d2e90]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(db): stores throw; delete Result and RepositoryError [c01d2e90]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(db): cancellation-safe catch-alls in DatabaseManager and DeferredEventPublisher [c01d2e90]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(db): outside-unit writes fail in production; guard and changelog [c01d2e90]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(db): independent P5b unit-of-work, outside-unit, poison and batch-granularity tests [c01d2e90]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(config): S11 asserts the in-block exception and the cause-translated unit outcome [c01d2e90]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(db): P5b review follow-ups - dependency delete fault shape, FAIL-policy wiring tests, docs [c01d2e90]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(db): FAIL-policy coverage for complete_tree, plan documents, claims and leases; doc fixes [c01d2e90]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rts, DialectNeutralityTest [P6] (#447)

* refactor(sqlite): move SQLite and Exposed code to infrastructure/sqlite [b094eb86]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(port): storage ports move to application/port as *Store [b094eb86]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(arch): DialectNeutralityTest with a ratcheted baseline [b094eb86]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(config): typed USE_FLYWAY and SCHEMA_MODE settings [b094eb86]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(arch): retarget the P5b retired-token guard; dialect rule extensions; doc names [b094eb86]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ance result [91544425] (4.0) (#449)

* fix(plugin): implement-wave requires and reports the entry seat's advance result [91544425]

The envelope schema requires entry for entering stages; seat prompts tell the seat to copy the advance_item result into entry. An agent stop on an entering stage is no longer masked as 'no entry report'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(plugin): implement-wave entry-report regression tests [91544425]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(changelog): qualify implement-wave entry-report bullet to seat entry mode [91544425]

Review observation O2: pre-entered mode still maps a missing entry to 'not pre-entered'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…nd clock, UTC timestamps (V18) [P7] (#450)

* feat(sqlite): UTC timestamp column type and V18 Normalize timestamps [beeef6f7]

One canonical UTC text shape for all 19 persisted timestamp columns; data-only V18 rewrites legacy datetime()/offset shapes. B1 of P7.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(sqlite): split the work-item store, bind one clock, total row mapper, claim predicate [beeef6f7]

Four store ports and a facade, claim/lease SQL bound to the unit instant (no database clock), one ClaimState predicate and SQL builder, claim columns out of update(), lease-history limit in SQL, total mapper returning invalid rows with diagnostics, placement/claim-predicate guards. B2-B4 of P7.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs: UTC timestamps, bound clock, claim predicate and store split [beeef6f7]

CHANGELOG entry, api-reference and api-rest updates for exact-expiry semantics, version-neutral claims, skipped always omitted and SQL-side lease history limit.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(sqlite): independent tests for bound clock, claim predicate, total mapper, UTC timestamps, V18 [beeef6f7]

Tests authored blind to the implementation from the frozen test-plan oracles: exact-expiry boundary across claim and lease surfaces, claim columns out of update(), no version bump, total row mapper, canonical UTC text under non-UTC default zones, and the V18 normalization over injected legacy shapes.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(sqlite): total mapper for unreadable timestamps; unconditional claim clear on terminal entry; review follow-ups [beeef6f7]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(sqlite): cover the missing-ancestor walk; re-claim tolerates unreadable original_claimed_at [beeef6f7]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(arch): placement-write guard matches any receiver name [beeef6f7]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…9] (#451)

* feat(idempotency): one idempotency service keyed per element [d1cccd1a]

Replace the in-memory requestId cache with a durable idempotency_records store (V19): each keyed element runs in one unit with its record, replays for 24h, mismatches on a different fingerprint, and records only committed results and payload rejections. REST advance and dependency POST honour Idempotency-Key; expired records are pruned hourly.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(idempotency): independent per-element idempotency tests [d1cccd1a]

Cover IdempotencyService, Fingerprint, pruner, SQLite store and V19 shape, keyed MCP tools and REST Idempotency-Key routes against frozen oracles.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(idempotency): restore weakened assertions; docs and schema text follow-ups [d1cccd1a]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(idempotency): close S5/S8/S10 gaps and tighten S2/S7 assertions [d1cccd1a]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* refactor(idempotency): persist created_at via the shared UTC timestamp column type [d1cccd1a]

Also corrects the api-reference idempotency principal sentence (review-r2 obs 3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(idempotency): principal scoping wording covers accept-self-reported [d1cccd1a]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…n events, SSE projection by seq [P8] (#452)

* feat(events): V20 events table, EventStore port and SqliteEventStore [ea2b9b63]

Adds the append-only events table (seq AUTOINCREMENT seeded at 1e12, four indexes, no FK), the EventStore port with RepositoryProvider.eventStore(), the SQLite store, and the upgrade-harness seed, baseline row, snapshot and migration-count updates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(events): typed DomainEvent catalog, EventRecorder, WriteScope.events and transition.rejected rows [ea2b9b63]

Adds the sealed DomainEvent catalog, the EventSink port with WriteScope.events on both unit-of-work implementations, the EventRecorder (unit instant, entity or ambient principal, proof status, commit listener), the recordRejection follow-up unit and the afterRollback helper, and records transition.rejected (gate_blocked, dependency_unmet) at AdvanceService's rejection returns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(events): record every mutating store call as events rows and project SSE from the table [ea2b9b63]

The decorator now appends typed DomainEvent rows through the shared EventRecorder in the caller's unit, is installed whether or not the REST API is on, and covers the lease, transition, project-config and plan-document stores (claim.rejected and lease.rejected in the committing unit, FK-cascade note and edge rows on item delete, two item.reparented rows per reparent). ApiEventBus becomes a projection of the events table: id = seq, a synchronous local tail on commit plus a 1 s cross-process poll, table-backed replay deduplicated by seq, and the F2/F3 gap rules (floor 1e12, API_SSE_BUFFER_SIZE replay window). The ring buffer, per-bus counter, rootsResolved and the per-transaction event buffer are removed; DeferredEventPublisher is the commit-signal adapter. TransitionStore.deleteByItemId is deleted (F4). Existing event tests are repaired onto the table (EventFeed / emit / FROM_START helpers); expected-value changes are listed in implementation-notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(events): document the events table and the SSE projection; KDoc corrections [ea2b9b63]

CHANGELOG [Unreleased] lists the REST/SSE wire changes (id = seq above 1e12, pre-upgrade cursors get sync.lost, API_SSE_BUFFER_SIZE is the replay window, rootId always present, per-note and cascade delete events, cross-process streaming, table-only rows). api-rest.md section 21/25, openapi.yaml and fleet-deployment.md drop the ring buffer and the local-writes-only limitation. KDoc that asserted the decorator is API-only or that update() emits item.advanced is corrected. claim.released (cleared) is now recorded only when a claim was actually held, since clear() reports true for any existing row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(events): independent P8 event coverage, rejection, replay and gap tests [ea2b9b63]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(events): adoption row, zero-row reads, keyed rejection rows [ea2b9b63]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(events): rejection rows survive a rolled-back keyed unit [ea2b9b63]

EventSink.recordRejection: under an ambient unit the row is appended in that unit AND a rollback hook re-appends it in a fresh unit of the unit's owner (ActiveUnit.owner, set by SqliteUnitOfWork), so a transition.rejected / claim.rejected / lease.rejected row is written exactly once whether the caller's unit commits or rolls back (idempotency-keyed elements roll back on the rejection). UnitOfWork.recordRejection and the decorator's claim/lease rejections use it. Regression tests: keyed MCP advance gate-blocked, keyed claim_item AlreadyClaimed, lease.rejected in a keyed advance, REST advance with Idempotency-Key.

Also review finding 4 in the decorator: releaseAllForItems is set-based (one active-lease pre-read, one bulk release) and the delete cascade pre-read reads note identities only (NoteStore.findRefsByItemIds, column-narrow in SQLite). RecursiveDeleteEventsBench (TO_PERF=1, serial) times a 500-item recursive delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(events): new SSE subscribers start at the tail; commit signal never waits on the tail lock; docs [ea2b9b63]

ApiEventBus.connect (used by GET /api/v1/events) catches the tail up under the tail lock before registering the subscriber, so a fresh connection never receives rows committed before it connected nor a queue_overflow caused by them; a queue_overflow sentinel whose dropped event lies inside the replayed/covered range is not emitted. With a tailer running, committed() never blocks on the tail lock or reads the table: a busy lock or a gap wakes the tailer instead. Docs: floor wording (strictly below 10^12 is unknown_event_id) in CHANGELOG, openapi.yaml and the EventRoutes KDoc; stale rootsResolved KDoc removed; CHANGELOG and api-rest list the no-op-update, control-event-replay and tag-scope cascade deltas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(events): replay/live boundary delivers each row exactly once [ea2b9b63]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(events): connect() never skips rows committed during catch-up; lapsed leases are expiries, not releases [ea2b9b63]

ApiEventBus.connect: with subscribers connected the tail advances only to what the pump read and fanned out (as replaySnapshot does); the newest seq is read only when nobody is subscribed, so a row committed between the last page read and a separate maxSeq read can no longer be skipped for existing subscribers. Regression ConnectCatchUpRaceTest (fake store committing a row inside maxSeq / after the last page). Decorator: the single-holder releaseAllForItem no longer records a null-key lease.released row when only lapsed rows were removed (an expiry, P14's), matching releaseAllForItems.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… racing the client [ea2b9b63] (#453)

Root cause (test timing, not production): since d9fd4b8 each bus.emit is a suspending SQLite write plus its own commit signal, so the SSE client can drain the 4-slot queue between rows, and the 150 ms settle can also let rows commit before the connection registers; either way no queue_overflow arises. Reproduced locally by adding delay(20) between emits: same failure, Got: []. The test now waits for the connection to be registered and commits all ten rows under one commit signal, whose fan-out never suspends, so the queue overflows regardless of client speed. Assertions unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#454)

* feat(notes): NoteCommandService owns note write policy [6adda27b]

One service applies role normalization, the 64 KiB byte cap, CRLF normalization, the schema-role rule and maxLength/note_limits for manage_notes, REST note PUT/DELETE and create_work_tree.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(notes): independent tests for NoteCommandService write policy [6adda27b]

Covers role normalization, byte cap, CRLF, schema-role, maxLength layering and config-unavailable across manage_notes, REST note PUT and create_work_tree.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(notes): review follow-ups - create_work_tree docs, last-wins warnings, lookup in unit, anchor precedence [6adda27b]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(notes): negative cases assert the rejection reason [6adda27b]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(notes): noteAnchors slices get the inline-note write policy [6adda27b]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(notes): in-unit note writes do not serve last-known-good config [6adda27b]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…abb69e2] (#455)

* feat(telemetry): call log, reqId correlation and CallLogWriter [8abb69e2]

Every MCP tool call and non-SSE REST request gets a reqId (_meta.reqId / X-Req-Id, MDC, events.req_id) and a call_log row (V21) written in batches off the request path.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(telemetry): call log, reqId correlation and CallLogWriter tests [8abb69e2]

Independent tests for the P10 call log: reqId, per-call telemetry, column derivation, writer, store, MCP and REST surfaces, V21 seed and snapshot, latency bench.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(telemetry): bound client-derived call_log text; review follow-ups [8abb69e2]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(telemetry): F1-F7 follow-up tests; stale raw-path oracles updated [8abb69e2]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(telemetry): REST call_log tool is the matched route template [8abb69e2]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(telemetry): REST tool oracles follow the matched route template [8abb69e2]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(telemetry): a 401 does not skip routing; principal_id differs for REST and MCP rows [8abb69e2]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(telemetry): resource set is derived from declared routes [8abb69e2]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… [983cb945] (#457)

Writing seat prompts gain an INTERRUPTED-ATTEMPT CHECK: snapshot uncommitted changes to refs/wip/<short>-<seat> via a temporary index, continue from them, never discard wholesale; shared mode scopes to owned files. Envelope gains optional interruptedAttempt and wipRef. Resume matrix, method-b.md and post-run.md add snapshot and cleanup steps.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…cy; previews via evaluate [P11 919d379e] (#458)

* refactor(advance): one unit of work driven by TransitionPolicy; in-unit cascades; previews via evaluate [919d379e]

AdvanceService now loads a TransitionSnapshot, evaluates the domain TransitionPolicy and applies the decision plus every cascade in ONE unit; get_context/gate canAdvance, get_next_status and the status graph derive from the same policy/table; one label policy (EffectiveConfigResolver.labelFor); RoleTransitionHandler, CascadeDetector and UserTrigger deleted.

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(advance): independent tests for the one-unit AdvanceService, policy gates, previews and status graph [919d379e]

Seat: test-author

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(advance): config resolved inside the advance unit; previews read missing notes from the decision [919d379e]

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(advance): config read inside the unit fails closed; previews report the decision's missing notes [919d379e]

Seat: test-author

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(advance): an in-unit config read fault fails closed as config_unavailable on the unkeyed path [919d379e]

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(advance): get_context preview fails closed on a read fault inside its unit [919d379e]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(advance): one write unit for cascading advances; non-poisoning store faults roll back the whole advance; keyed config-fault retry executes [919d379e]

Seat: test-author

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…flow burst [159331b0] (#460)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… instead of implementing it [62154ca8] (#462)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…4 5cd1086c] (#463)

* feat(claims): ClaimService owns claim and lease writes, events and expiry [5cd1086c]

Claim, release, clear and lease writes now go through one service that records claim.* and lease.* events itself and reports claim.expired / lease.expired (lazily on contenders and committing advances, plus an hourly sweep). The decorator overrides and EventPublishingLeaseStore are removed.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(claims): independent ClaimService event, expiry, sweeper and confinement tests [5cd1086c]

Blind test-author coverage of E1-E6, Rule X exactly-once expiry, the hourly sweeper and the store-write confinement guard.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(claims): duplicate resource keys acquire one lease and record one event [5cd1086c]

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(claims): ClaimService always carries the event log; latest-expired lookup selects one row; review follow-ups [5cd1086c]

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(claims): terminal and cascade expiry, lease history, sweeper wiring, stronger write confinement [5cd1086c]

Seat: test-author

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… normalization (V22) [P12 a19fcf0a] (#464)

* feat(dependencies): DependencyCommandService and dependency direction normalization [a19fcf0a]

IS_BLOCKED_BY becomes an input alias stored as BLOCKS; one service owns normalize, duplicate and cycle policy for manage_dependencies, REST and create_work_tree; V22 rewrites stored rows and tightens the type CHECK; readers move onto domain/graph (AR-03).

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(dependencies): independent tests for dependency direction normalization [a19fcf0a]

Cover IS_BLOCKED_BY normalization, restatement duplicates, one cycle rule, the blocked-side query filter, delete direction, fail-closed recommender and the V22 migration, authored blind against the frozen test-plan.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(dependencies): create_work_tree reports an invalid dependency spec as a validation error; docs [a19fcf0a]

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(work-tree): S7 expects the up-front validation error the M1 fix returns [a19fcf0a]

Orchestrator edit to a pre-existing characterization test: review finding M1 deliberately turned the in-transaction INTERNAL_ERROR into a VALIDATION_ERROR raised before any write. The domain-message and nothing-persisted assertions are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ral triggers; delete the event decorator [P15 947f0230] (#465)

* feat(items): ItemCommandService, one-statement subtree restamp and structural triggers; delete the event decorator [947f0230]

One service owns item create/patch/delete on MCP, REST and create_work_tree: create always lands in queue, a terminal auto-lifecycle parent takes no new children, reparent restamps descendants in one recursive-CTE statement, and reparent-out/delete re-evaluate the old parent in the same unit. Every write service now records its own events rows, so EventPublishingRepositoryProvider, DeferredEventPublisher and the work-tree executor are deleted.

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(items): ItemCommandService, placement invariant and store-write confinement tests [947f0230]

Independent tests for create-in-queue, closed-parent, subtree restamp, old-parent cascade re-evaluation, event rows and the work-tree unit, plus the placement invariant and store-write confinement guards.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* feat(notes): note.upserted records bodyFromFile; review follow-ups [947f0230]

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(items): bodyFromFile on note.upserted, attach-mode events, fan-out guard [947f0230]

Seat: test-author

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…es [P16 0c07190d] (#466)

* feat(errors): adopt the error catalog across services and both surfaces [0c07190d]

One LegacyMcpErrorMapper and one LegacyRestErrorMapper classify every failure by the catalog; AdvanceFailure.error is non-null; every MCP error carries kind; StatusPages is the /api/v1 safety net.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(errors): independent tests for error catalog adoption [0c07190d]

Blind-authored kind/errorKind coverage for MCP and REST, AdvanceFailure classification, the /api/v1 StatusPages safety net and a single-mapper source guard.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(errors): wrapped-fault probe follows the persistence-fault contract; StatusPages wiring and apply_failed kinds [0c07190d]

Seat: test-author

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… [P17 36c719db] (#467)

* test(contention): WAL contention suite and exact claim boundary tests [36c719db]

Adds exact-result advance, cascade, lease and claim races (in-process and across two DatabaseManagers) and the claim/lease expiry boundary above the store; replaces three weak store-level races. ServerComposition takes an injectable clock.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(contention): carry-ins - reparent race and restamp rollback, cross-process migration lock, lock policy, FK/reason asserts, production-unit bench, oracle orderings [36c719db]

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ario proofs [489440ce] (#469)

Bug fixes always get the revert-the-fix red-first run; new guard/gate/boundary/security behaviour gets a capped budget of about 5 decision-point mutations in one batched round; test-only, refactor, migration and glue items are skipped; real gaps escalate; surviving mutations are triaged. Machine contracts unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s (port of #459) (#470)

Cherry-pick of cc725eb from main onto release/4.0.

Port adjustments:
- test.yml keeps release/4.0's test-results/*/ report globs so serialTest results are published.
- current/build.gradle.kts hunk dropped: release/4.0 already runs maxParallelForks = 4 with
  per-fork SQLite isolation; the CI-only maxParallelForks = 2 override would reduce parallelism.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
jpicklyk and others added 27 commits October 16, 2026 20:27
…ed mode gating [8cd79bc0] (#598)

* feat(plugin): 4.0 context hooks, extensions.claude-code keys and shared mode gating [8cd79bc0]

The four injecting hooks spoke 3.x, read top-level plugin keys and ignored subagent and director sessions; they now share one suppression gate and 4.0 text.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): independent tests for 4.0 context hooks and shared mode gating [8cd79bc0]

Scenarios S1-S13 from the frozen test-plan, derived from the task-scope acceptance criteria: extensions.claude-code reader, director marker and one suppression gate, guidance versus diagnostics in session-start, orchestration-context and plan-hook gating, dispatch guard, injected-text hygiene and README mode-gating table.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(plugin): pre-plan and post-plan headings carry a real em dash, not double-encoded UTF-8 [8cd79bc0]

Amendment A1 arbitration: the blind author's S8/S13 tests were right; the implementation emitted mojibake.

Seat: implementer-a1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): dispatch-hint fixtures read orchestration.mode from extensions.claude-code [8cd79bc0]

Amendment A1: the declared existing-test edit moves from the implementer to the test-author seat.

Seat: test-author-a1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(plugin): orchestration-context says an answered decision needs an explicit resume [8cd79bc0]

Amendment A2 / review B1: an answer never resumes a blocked item (api-reference.md:2234-2235); retryAfterMs is optional (O5); principle 5 drops the role-filter clause (O4).

Seat: implementer-a2

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): orchestration-context snapshots follow the A2 decision sentence [8cd79bc0]

Amendment A2 / review B1: an answered decision needs an explicit resume; retryAfterMs optional; principle 5 trimmed.

Seat: test-author-a2

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): hooks README gating rows re-derived after the P10/P12/P13 merges [8cd79bc0]

Merge follow-up: the attribution hook row goes; retro, phase-guard, skill-enforcement, subagent-start and dispatch-hint rows match the merged sources.

Seat: implementer-a3

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(plugin): lifecycle skills as 4.0 recipes [facd8579]

Rewrites create-item, the plan workflows, schema-workflow, status-progression, batch-complete, dependency-manager and orchestrate as validated to-call recipes, ports the P30/P34 post-plan rules, and drops the eight pendingPort baseline rows.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): read batch failures from data.results[i].error [facd8579 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(plugin): dashboard and process skills on 4.0 [6018d76a]

Rewrite work-summary, session-retrospective, review-proposals and quick-start against 4.0 reads and writes as to-call recipes; add --run, the retro trigger paragraph and the legacy migration reference.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): retrospective and setup-adjacent skills match the 4.0 server [6018d76a A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…hemas, adopt-project-scope, deploy_to_docker [a28fa715] (#602)

* docs(plugin): setup skills speak 4.0 - init, configure-server, manage-schemas, adopt-project-scope, deploy_to_docker [a28fa715]

init registers the director entry and passes type project, manage-schemas validates before it pushes and drops the removed config keys, config-format keeps the authoring guide in 4.0 terms and links config-reference, configure-server and deploy_to_docker render MCP_ALLOW_UNAUTHENTICATED and bearer headers, adopt-project-scope drops the depth probe.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): setup skills match the 4.0 server on errors, types, ownership and config keys [a28fa715 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(plugin): task-orchestrator-mod speaks the 4.0 tools [5dbd8275]

Every mod read and matcher spoke 3.x and threw on 4.0: reads now go through the structuredContent envelope keyed by short ids, call-shape applies the 4.0 rule set, the phase guard records advance results, the live stream subscribes to the 4.0 event types with Last-Event-ID resume and decision/run glyphs, and retro follows run marks. Old mod tests migrate under the test-author seat.

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(plugin): task-orchestrator-mod tests for the 4.0 tools [5dbd8275]

Blind-authored scenarios S1-S17 (wire helpers, call-shape rules, phase guard, live stream and cursor, snapshot reads, graph-pane reads, retro run marks, catalog parity) plus the declared migration of the six 3.x test files to the 4.0 envelope shapes.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(plugin): mod graph-pane TDZ, errored-write marks, retro state filter, runs_write mark over REST [5dbd8275 A1]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(plugin): mod retro marks over REST and markRequest [5dbd8275 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): retro rig reads config in any path form under noApi [5dbd8275 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…njection [8aee9676] (#604)

* feat(plugin): director skill, altitude guard hook, and compaction re-injection [8aee9676]

Director mode needs a session marker the skill body cannot write, a PreToolUse guard that keeps the director at altitude, and a post-compaction reminder; this adds all three plus the hardened agent entry and docs.

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(plugin): director guard, context hook and skill tests [8aee9676 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(run-wave): port main twins P29 P30 P32 P33 P34 P37 to 4.0 [cac1bffa]

Mirrors e225afd 576b25b 850eaf6 aded4e2 95639ae 7dfbb2d (implementation and docs only; tests belong to the test author). Main-only carryIns support is not ported (not on 4.0, not in scope).

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(run-wave): coordinator on server runs - open, assign, end_seat, close; seat tokens by file [cac1bffa]

Port run-wave, implement-wave, review-wave, run-exec and run-planner to 4.0: runs_write/runs_read replace the run plan and state documents, seats read their token from a file, actor is removed (notes-audit replaces actors), prompts name 4.0 tools and teach per-element results, retry classes and the deviation raise; P31/P35/P36 prose twins and the P33 code hunks land.

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(run-wave): coordinator on server runs - run-ops, token-leak, ported twins and 4.0 edits [cac1bffa]

New run-ops and token-leak suites, the five main twin test files ported, and the declared edits to the existing coordinator suites for notes-audit, serverRunId, seat tokens, retry classes and the deviation raise.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(run-wave): AC3 exempts rule-seeding docs_write per line; plan-doc needles [cac1bffa A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(run-wave): resource contention reads error.detail.resources[].name [cac1bffa A1]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(plugin): seat agents and bundled rules on 4.0 [c6740063]

Seat agents and protocol rules drop 3.x tool names and the actor field, add seatToken, the deviation raise and the carry-in contract; manifest appends new hashes so config-sync re-seeds.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): guard seat agents and protocol rules on 4.0 names, seatToken and carry-in contract [c6740063]

Repo-reading guard over agents/*.md and bundled-rules/protocol.*.md against the generated tool catalog: no retired 3.x names or actor, seatToken, error-code coverage, to-call validity, deviation raise, carry-in dispositions, append-only manifest.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): implementer names decisionId; planner disposition template on one line [c6740063 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): entry-seat rule reads call-level unauthenticated/partial_failure; blocked takes resume, not start [c6740063 A2]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… run-ended retrospective [dbb45373] (#607)

* feat(plugin): coordinator tiers, altitude-safe implement-wave result, decision push, run-ended retrospective [dbb45373]

Director mode needs a coordinator launch profile per tier, a Workflow result safe to land in the director context, a code-enforced decision push and retrospective trigger, and the P20 guard gaps closed.

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(plugin): coordinator tiers, altitude-safe return, director watch and guard gaps [dbb45373 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): director guard allow-list probes - patch flags, status -v, quoted/expanded tokens [dbb45373 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(plugin): director guard uses flag allow-lists for git log/status/branch; quote-strip and expansion deny [dbb45373 A1]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…roject; freeze retired [2953cc30] (#608)

* feat(plugin): project skills and dogfood config on 4.0, floor gains project and retrospective-trend, freeze retired [2953cc30]

Project skills, rules and dispatch contract move to 4.0 tool vocabulary with main parity ported; the dogfood config takes the 4.0 extensions shape plus main's deltas; the global floor declares project and retrospective-trend; the dogfood-freeze job, script and test are removed.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(config): live dogfood STRICT, floor project types, project skills contract scans [2953cc30]

Independent tests for the 4.0 dogfood config shape, the floor gaining project and retrospective-trend, and zero retired 3.x tool names in the project skills and rules.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… means a missing note [66a5bc0c] (#610)

* fix(plugin): subagent-start protocol no longer claims a second start is harmless [66a5bc0c]

A second start moves the item forward; seats now read the role first and gate_blocked means a required note is missing.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): pin the subagent-start read-the-role-first protocol [66a5bc0c]

A second start moves the item forward; tests pin the role-first paragraph and gate_blocked as a missing required note.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…very [cae5d141] (#609)

* feat(ralph): one run per loop, seat tokens, outcome table from the error catalog, crash recovery [cae5d141]

Port the ralph drain to 4.0 runs: the driver opens one run, assigns an implementer seat and claims under its token per iteration, hands the token over as a 0o600 file path, ends the seat on every outcome (timeout and spawn error included) and closes the run; selection is items_read next plus a per-loop skip list; one OUTCOME_TABLE is rendered into the three prompt sources. Closes AR-75, AR-76, AR-66.

Seat: implementer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ralph): fixture loop, outcome table, skip list and render tests [cae5d141]

Independent tests for ralph on runs: subprocess fixture loop against a REST stub and a fake claude, OUTCOME_TABLE classification, skip list and selection, token hygiene, and the m3 whole-ERRORS-part assertion.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(ralph): redactTokens replaces longest tokens first; system prompt drops actor wording [cae5d141 A1]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ralph): all-permanent-skip page is drained; baseline guards accept an empty pendingPort [cae5d141 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(ralph): guard every pre-spawn signal window; transient unavailable/internal back off instead of permanent skip [cae5d141 A2]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ralph): pre-spawn signal windows and transient unavailable/internal classification [cae5d141 A2]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(ralph): consecutive idle resets only on a spawned iteration, bounding transient update/claim/assign backoff [cae5d141 A3]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ralph): persistent transient update/claim faults exhaust the idle budget [cae5d141 A3]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… the empty R3 section comment in the layering baseline [69091904] (#611)

W2 wave 6a feature review observations N1 and N2: CHANGELOG.md's V30 bullet carried a double parenthetical after the P5 docs fix, and layering-baseline.txt kept a trailing section comment with no entries under it. Wording-only; both ratchets stay header-only.

Seat: orchestrator

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…boots; empty-seed negative step [a58a1b7d] (#612)

* test(ci): upgrade smoke asserts one verified pre-4.0 .bak after first boot and after second [a58a1b7d]

The --seed smoke now proves the pre-upgrade backup is taken exactly once and verifies, and that a later boot adds no second copy.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(ci): empty-seed negative path for the upgrade smoke [a58a1b7d]

An empty seed must fail the upgrade smoke on both the row count and the missing pre-4.0 .bak, so a vacuous Part 3 is caught permanently on Linux CI.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(release-gate): name the upgrade smoke in the gate sentence and cite the V18 skip [a58a1b7d]

Review observations N1 and N3: docker-publish.yml runs only the positive smoke (the negative step is PR-only), and the no-second-copy claim is backed by the already-upgraded skip described under Upgrading from 3.x.

Seat: orchestrator

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ocked no longer recorded [b15ac560] (#614)

* fix(plugin): phase-guard-record records a seat already in its phase [b15ac560]

Seats read the role before start, so an in-phase seat sent no start and was never guarded; gate_blocked recorded the pre-transition role. Record the first context read of a phase-owner seat, drop gate_blocked.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): phase-guard-record read path and gate_blocked no longer recorded [b15ac560]

Covers the first-context-read recording rule for phase-owner seats and updates the gate_blocked and matcher tests to the new rule.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…, delete and reparent [3de8da24] (#616)

Covers P1 S7/S9 follow-up: suppressed review-pending / gate_blocked cascades with WORK-parent controls.

Seat: test-author-a1

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…583] (#615)

* fix(startup): take the migrate lock around backup plus migrate [126ec583]

The UPGRADE_BACKUP copy ran outside <db>.migrate.lock, so two first boots could both copy or one could migrate between the other's copy and chain. One lock acquisition now covers both; the lock is extracted to MigrationLock and re-entrant per thread.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(startup): backup plus migrate run under one migrate lock [126ec583]

Independent tests for DatabaseManager.backupAndUpdateSchema: one lock acquisition covers the UPGRADE_BACKUP copy and the migration, a racing boot waits then sees V>=18, backup failure and lock failure map to their outcomes.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(architecture): move the migrate-lock catch sites in the cancellation-safety baseline [126ec583]

The lock helper moved from FlywayDatabaseSchemaManager (4 -> 3 baselined sites) into MigrationLock.kt (0 -> 1); the two typed catches are the same code, so the ratchet line moves with it. No new violation.

Seat: orchestrator

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(startup): release the migrate lock with a typed catch; cancellation baseline shrinks by one [126ec583]

The lock helper moved the Flyway manager's runCatching { lock.release() } into MigrationLock.kt, where the cancellation-safety ratchet counted it as a new-file site and P5bWriteUnitGuardsTest S12 forbids new baseline files. Catching IOException on release (logged at WARN) is the compliant form, so the baseline line for FlywayDatabaseSchemaManager drops 4 -> 3 and no MigrationLock line is added.

Seat: orchestrator

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test(sqlite): make S7 no-lock-file assertion reachable [126ec583]

S7 now configures DATABASE_PATH inside the TempDir so a fallback to the configured path would create the lock file the assertion looks for.

Seat: test-author-a1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(startup): say the pre-upgrade copy runs under the migration lock; fix the lock KDoc antecedent [126ec583]

Review observations N2 (runbook sentence) and N5 (MigrationLock KDoc 'any other IOException' lost its antecedent in the move). Docs and comment only.

Seat: orchestrator

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* style(startup): wrap the MigrationLock KDoc to the 140-column limit [126ec583]

Review re-check observation N8: the N5 wording fix left a 173-character KDoc line.

Seat: orchestrator

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ade, validator warning condition [c7a1a497] (#617)

W1 phase 4 feature review (Fable) observations N1, N2 (server-docs half) and N5: CLAUDE.md:77 described the model-selecting override condition as the definition of a seat-only trait; bare-mcp.md:129 still said every parent auto-cascades to terminal (a review parent stays, reason review-pending, since P1 7cb8a3df); config-reference.md's order-warning wording now names the earliest overrider as the deciding one, matching ConfigValidator. Wording only.

Seat: orchestrator

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…; name SCHEMA_UPDATE for a migrate-lock timeout [5828514d] (#619)

W7 wave 6a feature review N2 and N3: AppConfig.kt:92 held a 0x0C form feed where #593 meant a backticked false (a collapsed backslash-f), and fleet-deployment.md did not say that a lock-timeout waiter fails startup as SCHEMA_UPDATE with no copy taken. Comment and docs only.

Seat: orchestrator

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ility table [4440ff65] (#618)

* docs(plugin): 4.0 plugin guides, consolidated Plugin 4.0 CHANGELOG entry, compatibility table [4440ff65]

Rewrites plugin-skills-hooks, orchestration-mode and self-improving-workflow for the 4.0 plugin, orders the CHANGELOG Plugin bullets by prefix and drops the interim unported-plugin claims, and fixes the cascade, actor_attribution and 422 claims in the server docs.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): config-format validator warning names the earliest seat override [4440ff65 A1]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): config-format validator warning keeps the model-selecting-after condition [4440ff65 A2]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…he 4.0 dispatcher [62478475] (#620)

The pinned MCP runtime-call-order sentence cited McpToolAdapter, deleted in 4.0; it now cites ToolDispatcher.dispatch (principal, capability, describe, selector, RequestDecoder, then withEventActor + withConfigSession). The harness: line numbers are refreshed too. Closes N3 of 62478475; N8, N10, N11 stay open.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ng notes [2bf68690] (#621)

* fix(plugin): phase-guard blocks only on the stopping seat's own missing notes [2bf68690]

Use gateStatus.missingBySeat plus the view's seats to avoid sending a seat back for another seat's note; falls back to the flat filter otherwise.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): phase-guard seat-bucket blocking scenarios [2bf68690]

Blind-authored scenarios S1-S12 plus probes: the hook blocks only on missingBySeat[seat] when the seat is known, else the flat filter.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(plugin): phase-guard header and README describe seat-bucket mode [2bf68690]

Review B1: the header still claimed TEST_AUTHOR_OWNED_KEYS are dropped independently of seat; it now describes bucket mode and scopes that filter to the fallback. O1: the README fallback clause notes the test-author exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…6bd76f76] (#622)

* fix(run-wave): deep-strip seat tokens from persisted seat envelopes [6bd76f76]

A seat echoing seatToken/coordinatorToken in notes or output landed it in run state; strip keys in both methods and redact token-file values in Method B.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(run-wave): token-strip coverage for seat envelope persistence [6bd76f76]

Blind tests for stripTokens, Method A callSeat, Method B stageResult and the stage-result CLI token-file redaction.

Seat: test-author

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(run-wave): stripTokens copies __proto__ as an own data key [6bd76f76]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(run-wave): stageResult signature names knownTokens; coordinator token is key-only in both methods [6bd76f76]

Review O1 and O2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ow [95c660e2] (#623)

* release: bump to v4.0.0, cut changelog, add major-release landing flow [95c660e2]

Version bumps (server 4.0.0, plugin 4.0.0, mod 0.2.0), CHANGELOG 4.0.0 cut with empty [Unreleased], prepare-release X1 flow, UPGRADE_BACKUP stamp and smoke wording fixes.

Seat: implementer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(plugin): CHANGELOG pin tests read the 4.0.0 section after the release cut [95c660e2]

Four tests pinned item bullets under [Unreleased]; the cut moves them into ## [4.0.0] - 2026-10-17, so each test now anchors on that heading. Oracles unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(release): landing steps cover the twin audit and the land/4.0.0 branch that absorbs main [95c660e2]

A simulated release/4.0 -> main merge conflicts in 60+ files, so the landing branch records main as a parent with -s ours after a twin audit (21/22 covered; #574 needs a 4.0 twin, item a3d6a64a). main's [Unreleased] 3.x bullets ship from release/3.x, not folded into 4.0.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(release): review N1-N5 and fold post-cut bullets into 4.0.0 [95c660e2]

N1 resolve-changelog path; N2 conflict count; N3 fold release/4.0 [Unreleased] into [4.0.0] before the landing leaves draft; N4 drop the never-released dogfood freeze guard from 4.0.0; N5 release/3.x pin recipe caveats. The #621 phase-guard bullet moves beside the other Fixed bullets in ### Plugin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…plementer [a3d6a64a] (#624)

* feat(run-wave): run-planner and implement-wave feed carry-ins to the planner and late carry-ins to the implementer [a3d6a64a]

4.0 twin of main #574 (d8f23c8).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* test(run-wave): blind carry-in plumbing tests [a3d6a64a]

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* docs(run-wave): page through the keyless carry-in list; behaviour-only CHANGELOG wording [a3d6a64a]

Review O1 and O7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
The guard rejected PRs from release/4.0 into main while main stayed 3.x. The 4.0.0 landing is that PR; 3.x maintenance continues on release/3.x.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ssed)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@jpicklyk
jpicklyk marked this pull request as ready for review October 22, 2026 22:16
@jpicklyk
jpicklyk merged commit 85f742e into main Oct 22, 2026
4 checks passed
@jpicklyk
jpicklyk deleted the land/4.0.0 branch October 22, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant