Skip to content

v0.76.0

Choose a tag to compare

@davidfarah2003 davidfarah2003 released this 09 Oct 14:50
42077d3

Cotal 0.76.0 is out!

Grab it: npm i cotal-ai@0.76.0

Changes in this release

  • The manager and the delivery daemon now name an injected SecretStore through one rule, injectedSecretStoreIdentity in @cotal-ai/core: the store's declared identity, else the coordinate in COTAL_SECRET_STORE, else a refusal. Each package kept its own copy before, so a change to one could have made the store challenge report two names for one shared store, and both now refuse an unnamed store with the same message. reloadStoreIdentityOf from @cotal-ai/delivery takes { injected: true, store } or { injected: false, identity }. The daemon's injected cred source no longer carries an identity, which nothing read and which was blank when COTAL_SECRET_STORE was unset. See the upgrading guide.

  • Refuse competing patched same-host consuming sessions without replacing their logical inbox owner. Preserve live control listeners and existing foreground actor material. Make fresh local foreground grants and departure cleanup generation-aware. This containment does not fence other hosts, broker URL aliases or older clients.

  • A detached cotal spawn <persona> --resume <id> --on <instance> without --agent now carries the session for the harness the target manager launches. The CLI used to pick the connector from --agent or its own COTAL_DEFAULT_AGENT alone, so a persona with agent: claude spawned by a caller whose default named another connector carried nothing, and a persona without a pin carried a Claude transcript to a manager whose own default then refused it. The CLI now asks the manager's new resolve-agent command, which loads the persona with the same visibility check as spawn and answers with the same flag, pin, caller default and manager default order. resolve-agent is minted beside spawn in the spawn capability and the operator instruments, and the manager cluster document moves to revision 26. With --agent the CLI does not ask, so a manager that does not serve the command still takes that form.

  • Comments in core, the manager and the runtime cite SPEC sections and code symbols instead of line numbers. Nine of those line numbers pointed at unrelated text, including the EpGateState.space doc in core's published types, which also named §13.9 for the per-space auth bucket that §13.12 defines. The comments now name the alias-reuse gate in the manager's spawn path, resolveService's instanceId option and the pinnedInstanceId it returns, the spawn affinity gate, the auth session ledger's reconcileSessionForTakeover, and each SPEC section without a line. No behavior changes.

  • A launch that resolves an empty read or post list now hands the session that empty list. aclEnv used to omit COTAL_SUBSCRIBE, COTAL_ALLOW_SUBSCRIBE and COTAL_ALLOW_PUBLISH when the list was empty, so a cotal spawn <persona> --subscribe , or --allow-publish , session fell back to the persona file's channels while its credential and launch record carried none. The launcher now sets the variable to an empty value, and configFromEnv falls back to the persona file or join link only when the variable is unset. A hand-driven session that exports one of these variables as an empty string now gets an empty list instead of the persona's.

  • An endpoint watching the channel registry now reports a registry entry it cannot decode as a warning that names the key and the decode error, and drops that entry from its cache. It used to keep the config it held before and report nothing, so channelDeliveryClass, the join backstop request and the leave tombstone acted on a delivery class the registry no longer held, while a fresh registry read already resolved the same entry to the default. A per-channel entry that does not decode now resolves to the space default, and a =defaults entry that does not decode leaves the built-in defaults in force, until a valid entry is written.

  • resolveService now bounds the whole resolve by its deadlineMs, contract store reads included. The describe spends from that deadline and each closure walk gets what remains of it, so a slow or stalled store read fails the resolve with deadline-exceeded at the caller's deadline. Each walk used to run under its own 30 second budget, so a resolve given 300 ms could take seconds. Cancelling a resolve through its signal now settles a store read in flight instead of waiting for it to return, through a new optional signal on fetchContractClosure. Every caller that resolves an endpoint inherits the bound, including invokeService, cotal describe, cotal invoke and the cotal status manager probe. A resolve given no deadline is bounded as a whole by the 10 second default the describe already used.

  • writeSecretFile now sets mode 0600 on the open file before it writes the bytes, so replacing an existing file no longer keeps that file's mode on POSIX. The mode passed at open applied only when the call created the file, and a secret written over a 0644 file stayed 0644. This covers cotal mint --out over an existing credentials file and cotal mint --signer --force.

  • writeSecretFile and the create-only secret write now share one private write path, so the Windows rule that removes a file whose ACL could not be hardened lives in one place. The reason the create-only path checks the name with lstatSync on Windows is now written once, in its doc comment. No runtime change.

  • deregisterServiceInstance no longer deletes a successor's status. When a restart of the same instance re-registered and wrote its ready status between the deregistration's spec read and its status read, the deregistration deleted that status and reported superseded, so the successor kept serving while every class scatter, cotal ps included, skipped it as never converged. A status that observed a later registration than the spec read is now left alone. The superseded contract, the cotal deregister-instance refusal and the CLI docs no longer say nothing was removed: the spec stays, and a status that was removed observed the inspected registration or an earlier one.

  • An endpoint now marks every refusal it raises before running the command with outcome: not-executed, as SPEC 13.3 requires. Before, only the bind fence, the unmapped-target refusal and the args-schema refusal said so, and a wrong envelope version, sender, operation, contract digest or target mode, a missing deadline, a refused target or authority check, or a body that is not JSON came back with no outcome, which a caller has to read as unknown. The serve loop now decides the outcome from whether it has dispatched to the handler, so a check added ahead of the handler is covered too. A refusal the command itself raises is unchanged. A refusal whose reply does not serialize or is too large to publish, which comes back as internal or resource-exhausted, now keeps the outcome it stated instead of dropping it.

  • @cotal-ai/core exports REMOTE_MANAGER_AUTHORITY_OPERATIONS, the frozen list of manager-service authority operations, and RemoteManagerAuthorityRequest["operation"] is derived from it. The auth request parser checks the operation against the same list and builds its refusal from it, so an operation added to core is admitted by the host instead of compiling cleanly and being refused at runtime with the old list. The refusal now reads operation must be one of prepare, activate, renew, session, retire, renewStandingBundle, renewRunDriver, transferReader.

  • An OpenCode seat whose turn outlasts the consumer's ack wait no longer loses the role request it is working on to another holder of the role. The connector acks a batch only when its turn ends, and the 60-second ack wait used to run out under a longer turn, so the broker handed the same anycast to a second worker while the first was still on it. The batch a turn carries is now held in flight, and while it is held the agent restarts each delivery's ack wait with JetStream's in-progress ack at half the wait. A failed turn or a stop releases the batch and a crashed seat stops renewing it, so it redelivers after one ack wait as before, and a success or a user Stop acks it. Queued messages are not renewed, and the bounded inbox no longer evicts a held message to make room for them, which had stopped its renewal and left the finished turn nothing to ack. Renewal stops for good once the agent can no longer vouch that it still holds a copy: the wait ran out between renewals, the link was down past it, or the copy's connection was torn down. A durable Delivery now carries redelivery (the broker's delivery count, the consumer's ack wait, and working()), an InboxItem carries deliveryCount, and a frame marks a copy the broker delivered more than once as redelivered. Delivery stays at-least-once: a request can still run twice, so work with side effects that must not repeat needs its own ownership or idempotency key.

  • The cotal config reader now refuses a spawn that is not an object and a spawn.env that is not a list of non-empty names, with the file and the field named, as it already does for modelPolicy and connectors.<name>.mcpServers. Before, a string spawn.env was spread into one-letter names, so a seat received every one-letter variable the string spelled and not the one the operator named. A non-string entry or an object crashed every spawn and resume with a bare TypeError, and a non-object spawn was ignored.

  • The spawn-name actor-token smoke parses the CLI's spawn.ts and reads spawnNameError from the value imports of its @cotal-ai/core import declarations, so import-shaped text in a string or comment does not count. The cell used to search a fixed 400-character window before the first such import's closing brace, so it failed whenever the import list grew and pushed the name out of that window.

  • A conclave now asks the host's stop before it opens, as every other effect does. A run whose driver has asked it to stop (an operator pause or a passed work horizon) is released (L5012) at the next conclave instead of opening it, and a program made only of conclaves no longer runs to completion after the stop. A settled conclave still replays under a stop.

  • PRIMITIVES, EVENT_CONSTRUCTORS, PURE_PRIMITIVES and FORBIDDEN_NODES are now frozen through every row and nested array. A write to a row, or to the callee a validation LangError carries, throws instead of changing what validate accepts or reports for the rest of the process.

  • cotal status and the cotal setup card now share one test for whether the web extension is installed: a package in the extensions manifest that provides web and is on disk. A manifest entry whose package directory is gone no longer reads installed in status while the card reads not installed. A web package record that cannot be read is no longer reported as not installed: status names the error on its Web extension row and still prints the Web process row, and the setup card names it on its web row, also while the dashboard is listening. loadExtensionsManifest and installedExtensionVersion now treat only a missing file as absent, so an extensions or package directory that cannot be searched throws its permission error instead of reading as no extensions or not installed.

  • cotal status --components now names a web.session that exists but cannot be read, for example after an ownership or mode change: the web row reads refused · web.session unreadable: <error>. The reader used to treat every read error as no record, so a live dashboard read refused · probe refused (no bound address recorded), and the Web process row and the cotal setup card read down. Those two rows now name the unreadable record as well. Only a missing, empty or partial web.session still reads as no record. cotal web --detach prints the launch link from the record that proved the dashboard ready instead of reading web.session again, so a read error after that point cannot report a failed start over a running dashboard.

  • loadSeatWriterGeneration reads each seat writer generation through the same record reader as every other auth record. A generation entry that is a symlink, a directory or a FIFO is now refused as not a regular file instead of being followed, so cotal down --preserve-state no longer stamps a checkpoint with a generation read through a link. A generation file that does not parse is reported in the shared record message.

  • Keep MCP alive while event recording waits for an initial mesh connection or a reconnect. Resume queued event publication when both the mesh binding and transport recover, and cancel the wait on shutdown without weakening required-recording failures.

  • The Claude Code event mapper now exposes only what the connector calls: map and forgetOpenRun. It drops closeOpenRun, openRun and diagnose, which nothing shipped called. The Stop and StopFailure hooks close a session's last run through the event holder, and the mapper's header now says so instead of saying that run never closes. No behavior changes.

  • Correct workflow tool guidance for managed user-auth agents using their own issued participant-manager authority. Preserve run capability and unsupported-host refusals.

  • An OpenCode launch no longer refuses to start because of a recorded server that exits during the liveness check. ps also fails for a pid that is gone by the time it runs, and that failure used to read as a server the platform could not inspect, so the launch failed with agent "<name>" is already running. The check now probes the pid again after a failed ps, and only a pid that is still present keeps the fail-closed answer.

  • An OpenCode agent launch no longer fails with EISDIR when an empty directory sits at the agent's serve.pid. The launcher now treats that directory as no record, removes it, and starts the server, and a directory that vanishes before that removal also counts as no record. A directory that holds files is refused with ENOTEMPTY and left in place, and an unreadable record still fails the launch.

  • Remove allowManagerAuthority from the exchange policy that handleManagerServiceAuthority takes. Both exchange faces set it to true, so its refusal could never fire, and the route table alone decides which face serves POST /manager-service-authority. The docs and source comments now list that route on the public listener, which has always served it. A policy object literal that sets allowManagerAuthority no longer compiles, and a JavaScript caller that set it to false is no longer refused by the handler; see the upgrading guide.

  • Issue managed seats' manager-caller run authority when their current persona scope and fresh same-owner parent grant both carry run. Preserve the signed permission ceiling, actor-row lifecycle source, interactive issuance and ordinary legacy commands.

  • Add an owner-bound platform supervisor authority door to the hosted auth service. It reuses native manager registration and renewal, checks a current administrative assignment, confines issuance to one owner and lifecycle, and records the registration credentials' signed permission ceilings before returning material. Recorded permissions match the authority-plane signed native set at issuance and every renewal without widening. endPlatformSupervisorAssignment(owner) ends an assignment the host recorded ended: it retires the manager gate and the issued generations, so registration, activation and renewal refuse previously returned material by name before it expires.

  • cotal actor revoke now mints its live-connection eviction credential with a 60 second lifetime instead of the supervisor profile's 24 hour default. The revoke-time eviction, the barrier evictors and the liveness oracles that call the delivery daemon's ctl.delivery-admin rail now share one connection helper, so the lifetime and the non-participating endpoint options are decided in one place.

  • The auth-ledger and records scanners now run every sealed scan through one shared plane-guard helper, guardedScan, beside ScanGuard. It used to be written out three times: once in the ledger scanner and once in each of the records scanner's obligation and manager goal-index scans. A change to the before-and-after claim check is now made in one place. No behavior changes.

  • A renewRunDriver renewal now checks the run it renews through one holdsRunFence predicate in @cotal-ai/auth, read through one run observer in the auth service. The renewal decision and the re-check right before issuance each kept a hand-written copy of the read and of the run fence comparison, so a change to what makes a run renewable could reach one copy and leave the other weaker or stricter without any suite noticing. Both still refuse with their own conflict message.

  • The hosted auth fence smoke takes its injected-store baseline after each plane's first start, which now puts the plane's instance identity into the store, so its store cells check that the fence and close write nothing on top of that record.

  • The CLI now decides whether a durable cotal is on PATH with the shared resolveOnPath, in one place. Before, cotalOnPath() kept its own scan that counted a directory named cotal as installed, so with such a directory on PATH, or with . on PATH in a folder holding a cotal/ checkout, every hint printed a bare cotal ... the shell could not run and npx cotal-ai setup skipped the global-install offer. On Windows it now follows PATHEXT like every other PATH lookup. The recovery hint for an older binary no longer names npx's transient cotal shim when . is on PATH.

  • A static-auth foreground cotal spawn now deletes the agent's broker durables and read-ACL row when it exits even if its creds file cannot be removed. The retirement used to stop at a failed file removal (a read-only creds directory, a directory in the file's place), so the agent's dm_ and dlv_ durables and ACL row stayed on the broker and the removal error escaped the spawn. The secret delete, the file removal and the broker teardown now each run, and every failed step is reported in one line.

  • A foreground cotal spawn now resolves its --share-tools selection before it provisions the agent. A selection naming a server the config does not declare used to refuse after provisioning and outside the launch rollback, so the refusal left what provisioning had created behind, such as a remote user-mode spawn's actor-token and sentinel-creds files. The refusal text is unchanged. The spawn also reads the cotal config once, so the model policy and the launch use the same file contents.

  • A manifest broker.idp that embeds credentials (https://user:pass@host/... or the token form https://token@host/...) is now refused with broker.idp must not embed credentials, the same rule broker.servers already followed. It used to pass validation, and cotal up -f --dry-run printed the credential in its plan line. The refusal names the field and never repeats the value.

  • cotal service status now refuses a service unit or launchd agent that records its mesh but no absolute root, with the uninstall-and-reinstall remedy. Before, --json reported a unit with no root as installed with no root and no manager record, the human output crashed with Cannot read properties of undefined (reading 'state'), and a recorded root that was not an absolute path read the manager pidfile relative to the current directory. An installed status now always carries its unit, root and manager record.

  • Foreground cotal up and cotal up --detach now run the steps between spawning nats-server and serving it through one sequence: .cotal/nats.pid, readiness, the broker version read and the server name check of a resumed or restored listener. A foreground up whose broker is below the 2.12 floor used to exit with the broker still running and .cotal/nats.pid recorded but no mesh entry. It now stops the broker and removes the pidfile, as --detach did. A foreground resume after cotal down --preserve-state now refuses a spawned listener that reports a foreign NATS server name, which only --detach checked before. A foreground up whose broker never answers now removes .cotal/nats.pid itself. up --detach now writes .cotal/nats.pid as soon as the broker spawns, as foreground up does, so cotal down can stop a broker whose up died before it was ready. When any of these failures stops a broker that ignores SIGTERM, up now sends SIGKILL and removes the pidfile only once the broker has exited, where both modes used to exit with it still holding the port.

  • The useMesh() header comment, which ships in the CLI's dist/console/mesh.js, no longer says the MeshView model lives in @cotal-ai/core. It now points at ../view/mesh-view.ts in the CLI package, where the class is defined. No runtime change.

  • The startDeliveryService handle's close() and drain() now reject when the instance did not give its shard lease back: the lease row is still held after the close, by another holder or because the release did not commit, or the broker could not confirm the release. They used to resolve in every case, so a host that started a successor at once could be refused with "a live lease already exists" without any prior signal. The rest of the teardown still runs, and the bucket TTL still expires a row the instance left. A failed start keeps its own error and logs a release that also failed.

  • cotal feedback-intake now refuses a --port that is not a decimal number from 1 to 65535 before it probes the broker, with an error that names --port. An out-of-range port such as 70000 used to pass argument parsing, so the intake connected and announced itself on the mesh before listen() failed with options.port should be >= 0 and < 65536. A hex or exponent spelling such as 0x1f90 or 8080e0 was accepted and bound that port.

  • Add explicit pooled ManagerOptions.execution: "none" with no Runtime object, early seat and workflow refusals, and immutable per-instance execution admission. Native control registration, renewal, goal recovery and maintenance remain available. Genuine selected participant Managers keep their own execution and terminal custody. Default runtime behavior is unchanged.

    Manager cluster revision 25 requires execution, runHosting and terminalSessions in status and adds custody none. Callers must fetch the new closed output contract.

  • An ask attempt and an escalated checkpoint now relay the instant their pause denies at, and the manager holds the relay to that instant. The relay used to send a duration the manager counted from its own acceptance, so the manager could still serve an attempt after its pause had expired, by the submit's round trip plus clock skew, or by up to a second from a one-second floor. The manager's turn command takes deadlineAt as an alternative to deadlineMs and refuses a relay whose deadline has already passed with deadline-exceeded. The run reads that refusal as nobody left to tell only when its own clock also shows the deadline passed. An ask whose deadline passes before its attempt's pause is minted now ends with its own ask-deadline L4006 instead of an L4000 from the refused mint.

  • cotal deregister-instance and the manager's clean stop read the issuance-gate generation through core's readEndpointGateGeneration. When the instance holds its governance slot and its gate is absent or carries a delete marker, the refusal's diagnostic text now comes from core's reader rather than a hand-rolled "no issuance gate" message. The error class is unchanged: core still wraps the reader failure as unavailable.

  • The events-grant smoke's two own-channel spawns now keep the persona's work read set next to the foreign event channel, so the read-list check admits them and the own-channel rule is what refuses them. The suite is green again and its section 9 and open-mesh cells run.

  • Refuse spawning another principal's owned persona on user-auth meshes unless the authenticated caller has current admin reach. Hosted runs use their admitted caller and the existing catalog visibility policy before connector loading or enrollment. Unowned operator personas and independently authorized manifest launches keep their behavior.

  • The manager's spawn provisioner connection now opens through the same withScopedEndpoint helper as the delivery-admin evictors and the liveness probe. Before, it built its endpoint by hand with no error listener, so a connection error on it, such as a broker permission violation during onboarding, ended the manager process with an unhandled error event, and a failed stop replaced the onboarding result or error with the stop error. The provisioner credential keeps its five-minute lifetime.

  • The manager's remote requests now take their identities record from one function, publicIdentities, instead of nine hand copies. Seven of the copies built the record from the identity state's own keys behind a type cast, so a change to the identity set still compiled there and sent a record the host refuses, and a state with its keys in another order sent identities that the manager's own result checks would not match against the host's echo. The function names the five identities in the order the host echoes them, so a change to the set now fails to compile at that one place.

  • A name held pending retirement keeps the departed seat's whole teardown target, and a same-name spawn or a resume re-drives that target unchanged. Before, both re-drives rebuilt it field by field and dropped the seat's recorded runtime reference, so under tmux a same-name spawn re-drove a static retirement that skipped the reap and freed the name while the predecessor seat still ran. The resume re-drive also dropped the launch's subscribe channels, which name the membership rows its teardown deletes.

  • Fix a hosted checkpoint with onExpiry: "escalate" failing L4000 every time its first attempt expired. The escalated attempt read the first attempt's binding as its own, so it armed at the first attempt's already-passed deadline and never recorded its own. Each attempt now binds its index with its deadline: the escalation opens with a fresh timeout, records its addressee and deadline, and a resumed attempt keeps the deadline it recorded.

What's Changed

  • fix(cli): stop the manifest broker.idp refusal from printing the value by @davidfarah2003 in #3333
  • fix(orca): bound the driver's read-only CLI calls so a stalled Orca app cannot block the manager by @davidfarah2003 in #3334
  • fix(website): resolve docs site links by repository path by @davidfarah2003 in #3338
  • fix(connector-core): refuse unrecognised values in the connector on/off env flags by @davidfarah2003 in #3343
  • feat(linear): serve Linear's hosted MCP server as a Cotal endpoint by @davidfarah2003 in #3339
  • fix(core): freeze REMOTE_MANAGER_IDENTITY_NAMES at its declaration by @davidfarah2003 in #3346
  • fix(examples): refuse an unknown persona in face-term instead of rendering ray by @davidfarah2003 in #3349
  • fix(manager): state the readiness latch ordering in one short comment by @davidfarah2003 in #3350
  • fix(core)!: remove Pane.confirm and the layout providers' blind Enter timers by @davidfarah2003 in #3354
  • fix(runtime): route every poll and retry wait through one unref'd delay by @davidfarah2003 in #3358
  • fix(core): describe the ep rows each control credential holds in the provision comments by @davidfarah2003 in #3359
  • fix(auth): check the manager-service authority and maintenance envelopes through the shared core parser by @davidfarah2003 in #3364
  • fix(web): judge the dashboard pidfile with the workspace pid contract by @davidfarah2003 in #3367
  • fix(web): state the activity aggregation and membership route contracts in the present tense by @davidfarah2003 in #3366
  • fix(remotion): pick the unicast card's addressee by name by @davidfarah2003 in #3374
  • fix(connector-opencode): state the teardown rule without counting its callers by @davidfarah2003 in #3377
  • fix(cli): state the ext remove labelling rule in one comment by @davidfarah2003 in #3383
  • fix(remotion): draw the multicast and anycast fans through one shared wire helper by @davidfarah2003 in #3385
  • fix(remotion): sequence each explainer snippet at the length it exports by @davidfarah2003 in #3384
  • fix(remotion): remove the unused variant 2 wordmark and mark from the header helpers by @davidfarah2003 in #3389
  • fix(seat): record an unmatched startup confirmation as the exit diagnostic by @davidfarah2003 in #3390
  • fix(remotion): measure the anycast reviewer bracket from the peer pool by @davidfarah2003 in #3392
  • fix(cli): name an ambiguous process record on its status row instead of aborting by @davidfarah2003 in #3393
  • fix(core): check the manager-caller bearer binding in one core function by @davidfarah2003 in #3391
  • fix(core,auth,delivery): check account nkeys through one exported predicate by @davidfarah2003 in #3399
  • fix(connector-core): drop the reviewer handle from the JsonlFileSource seal doc by @davidfarah2003 in #3406
  • fix(connector-core): state the principal lock's release boundary without its earlier-implementation narration by @davidfarah2003 in #3409
  • fix(connector-core): state the subject-frontier comments as current rules by @davidfarah2003 in #3415
  • fix(auth): keep the hosted auth plane's instance identity in the injected store by @davidfarah2003 in #3413
  • fix(core): drop plan labels from the goal-writer reconcile-index and own-gate comments by @davidfarah2003 in #3416
  • fix(core): verify cluster manifests with the contract store's closed parser by @davidfarah2003 in #3420
  • fix(core): share one lazy service contract table across auth, manager and linear by @davidfarah2003 in #3423
  • fix(remotion): read the wordmark width from WM_WIDTH at every call site by @davidfarah2003 in #3421
  • fix(connector-core): name the DurableSource.read contract in the WAL frontier guard comment by @davidfarah2003 in #3424
  • feat(auth): revoke hosted runs through the issuing host by @davidfarah2003 in #3419
  • fix(manager): report a refused runtime stop on shutdown and failed resume by @davidfarah2003 in #3427
  • fix(cli): read the launchd provenance header back through the comment syntax that wrote it by @davidfarah2003 in #3437
  • fix(manager): free a self-exited seat on runtimes that cannot attach by @davidfarah2003 in #3432
  • fix(web): escape dashboard text through one escaper shared from parts.js by @davidfarah2003 in #3438
  • fix(remotion): pick the anycast claimer from the shared presence by @davidfarah2003 in #3442
  • fix(workspace): read seat writer generations through the shared auth record reader by @davidfarah2003 in #3446
  • fix(cli): point the useMesh header at the MeshView model in the cli package by @davidfarah2003 in #3448
  • fix(core): mark every refusal the serve loop raises before dispatch not-executed by @davidfarah2003 in #3453
  • fix(cli): name an unreadable web.session instead of reading it as no recorded address by @davidfarah2003 in #3455
  • test(auth): take the hosted fence smoke's store baseline after each first start by @davidfarah2003 in #3456
  • fix(auth)!: drop the manager-service authority flag that no face turns off by @davidfarah2003 in #3463
  • fix(auth): derive the manager-service authority operations from one core list by @davidfarah2003 in #3464
  • feat(auth): owner-bound platform supervisor authority with an ended-assignment fence by @davidfarah2003 in #3462
  • refactor(auth): run every sealed scan through one plane-guard helper by @davidfarah2003 in #3466
  • fix(cli): decide whether a durable cotal is on PATH through the shared resolver by @davidfarah2003 in #3467
  • fix(manager): build remote request identities in one typed function by @davidfarah2003 in #3469
  • fix(connector-opencode): read a recorded serve that exits before ps runs as not running by @davidfarah2003 in #3472
  • fix(delivery): refuse a feedback-intake --port that is not a decimal port from 1 to 65535 by @davidfarah2003 in #3473
  • refactor(core): give secret-fs one fail-closed write path by @davidfarah2003 in #3474
  • fix(cli): refuse a service unit with no absolute recorded root in service status by @davidfarah2003 in #3477
  • fix(runtime): hold an ask's relay to the deadline its pause denies at by @davidfarah2003 in #3478
  • fix(core,manager,delivery)!: name an injected SecretStore through one core rule by @davidfarah2003 in #3480
  • fix(cli): share one web-installed predicate between status and the setup card by @davidfarah2003 in #3482
  • fix(core): refuse a malformed spawn block when the cotal config is read by @davidfarah2003 in #3483
  • fix(release): refuse a package with no npm record before the OIDC exchange and derive the publishable set by @davidfarah2003 in #3481
  • fix(lang): deep-freeze the exported syntax tables so a row write cannot change validation by @davidfarah2003 in #3487
  • fix(core,manager,runtime): cite SPEC sections and symbols instead of line numbers in comments by @davidfarah2003 in #3486
  • fix(connector-claude-code): narrow the event mapper to what the connector calls by @davidfarah2003 in #3488
  • fix(core): bound the whole resolveService by its deadline, store reads included by @davidfarah2003 in #3485
  • fix(auth): check the renewRunDriver run fence through one predicate by @davidfarah2003 in #3491
  • fix(connector-opencode): remove an empty directory at serve.pid instead of failing with EISDIR by @davidfarah2003 in #3490
  • fix(cli): refuse a manifest broker.idp that embeds credentials by @davidfarah2003 in #3497
  • test(core): parse the spawn-name smoke's CLI core imports instead of matching text by @davidfarah2003 in #3498
  • fix(core): leave a successor's status alone when deregistration races its registration by @davidfarah2003 in #3499
  • fix(core): set a secret file's mode before writing over an existing file by @davidfarah2003 in #3500
  • fix(manager): re-drive a retirement hold with its whole teardown target by @davidfarah2003 in #3501
  • fix(connector-core): forward an empty launch ACL list instead of dropping it by @davidfarah2003 in #3503
  • fix(runtime): bind each checkpoint attempt's own deadline so an escalation opens by @davidfarah2003 in #3504
  • fix: preserve session custody and MCP reconnects by @davidfarah2003 in #3505
  • fix(connector-opencode): keep a running turn's role request from reaching a second worker by @davidfarah2003 in #3509
  • feat(auth): issue run authority to eligible managed seats by @davidfarah2003 in #3510
  • feat(manager)!: add explicit pooled no-execution control by @davidfarah2003 in #3511
  • fix(manager): enforce owned persona visibility before spawn by @davidfarah2003 in #3512
  • test(connector): verify managed agents drive owned workflows by @davidfarah2003 in #3513
  • fix(auth): bound the revoke-time eviction credential to 60 seconds by @davidfarah2003 in #3517
  • fix(manager): read the deregister issuance gate through core's reader by @drakeo338 in #2694
  • fix(cli): carry a detached resume for the harness the manager resolves by @davidfarah2003 in #3520
  • fix(manager): open the spawn provisioner connection through the scoped endpoint helper by @davidfarah2003 in #3521
  • test(manager): keep the persona read set in the events-grant own-channel spawns by @davidfarah2003 in #3525
  • fix(core): report and drop an undecodable channel registry entry by @davidfarah2003 in #3530
  • fix(lang): ask the host stop before a conclave opens by @davidfarah2003 in #3532
  • fix(cli): run foreground and detached up through one spawn-to-ready sequence by @davidfarah2003 in #3538
  • fix(delivery): reject a hosted close that did not release the shard lease by @davidfarah2003 in #3537
  • fix(cli): run the foreground retirement's broker teardown when the creds removal fails by @davidfarah2003 in #3540
  • fix(cli): resolve foreground --share-tools before provisioning by @davidfarah2003 in #3543

Full Changelog: v0.73.0...v0.76.0