Skip to content

Releases: paracoding-ai/paracoding-oss

v14.0 — the mode, and the receipt

Choose a tag to compare

@paracoding-ai paracoding-ai released this 08 Sep 14:01

Release tree at source commit 9682c951. Thirteen paths move; the rest are byte identical to v13.4. The published tree object is the same sha1 as the source repository's own oss/release tree — 6f2f920886df24376eb4a80bcec24a026e684164 — so bytes and modes match by construction rather than by inspection.

./install.sh works on a clone now

All fourteen shebanged files in this release ship 100755. They shipped 100644 in every release before this one, so ./install.sh was permission denied on the first command every document names, and only bash install.sh worked. RELEASING.md step 3 has recorded that failure since v4.2: "every check took the bash route, which structurally cannot observe the mode."

It was never neglect. It was inexpressibility — the generator emitted those files executable on every single build, wrote the correction to a bucket, and no layer of the stack could apply what the generator itself produced.

A file mode is expressible, end to end

buildTree took no mode parameter, so nothing above it could have one. That is closed now at every layer: the tree builder, the whole-file write, the scoped HTTP release route, and the unified-diff applier — including the pure chmod git emits with no ---/+++ headers and no @@ hunks at all.

Omitting a mode leaves it alone everywhere, so rewriting an executable does not quietly disarm it, and a copy carries its source's mode. 100644 and 100755 are the only accepted values; anything else is refused by name, because a symlink and a submodule are different kinds of tree entry rather than modes of a file.

Closing a work item costs a commit the server resolves

An agent reported six review items complete, with detailed per-item "Actions Taken", and none of it had happened — no commit, no document changed, all six still pending. A human caught it by hand. Nothing objected, because opening an item already demanded cited evidence and closing one demanded nothing.

complete_work_item now requires an evidence_oid that the server resolves against its own object store, on both the tool surface and the console chat's table — a gate on one of two doors is not a gate. The agent cannot forge it because the agent does not perform the lookup. Not a reviewer: a reviewer is handed strings the agent composes, and an agent that fabricates a completion fabricates its evidence too. evidence_oid:"none" with a reason of substance remains for measurements and negative results, recorded on the item and named in the journal.

The advisor can verify what it is told

Prompt caching now marks the system block and the tool schemas, where the stable tokens actually are; the breakpoint had been sitting on the second-to-last message, which resolves to nothing for a single-message caller. Measured on the deployed revision: cache_read=21912 per round against a flat zero before.

That paid for the round budget, 1 → 6. At one round the advisor spent everything it had establishing identity and reading one item, and opened its answer with "I could not verify the claim itself — I made no repository read." An advisor that cannot check what it is handed is not cheap, it is decorative.

The release tree stops being thrown away

The pipeline regenerated this tree on every build for months and never committed it. The build now commits it through the one scoped route that can move a branch, sending only the files that differ, under its own attested identity.


Cut deterministically: gen.py run twice into separate directories, diff -r reports zero. Bus gate 0 (negative control 4/4); passkey gate 0 (negative control 7/7, four asserted allowances). check-apis, check-python-names, check-no-embedded-assets and bash -n on all three scripts green against the emitted tree, not the source.

leak-baseline.json is gone from the release — it is a repository input the generator reads for its leak ceilings and never emits, and it had been riding along from an older cut.

Apache-2.0. Your project, your bill, your key.

v12.8 — a machine can prove who it is without holding a secret

Choose a tag to compare

@paracoding-ai paracoding-ai released this 06 Sep 03:42

Full release tree at commit 5decaca38d5f8a886ba7883e06dae3867739327e of the source repository, published by the product's own gh_commit reading each blob straight out of its own git store. MANIFEST.txt lists every file and its digest. 103 files. Seven paths move; the other 96 are byte identical to 12.7.

An agent no longer pastes a session key

This install's MCP connectors are account-level: one OAuth bearer serves every chat, so the server has no signal that tells one chat from another, and a chat proves which strain it is by presenting a server-minted session key as the agent argument on every call. That is right for a person in a chat and it does not change here. It is wrong for a machine that is one strain forever, because it makes that machine carry and rotate a shared secret to say a thing Google can already attest.

So: a caller presenting a Google-signed service-account ID token now resolves to the strain that service account is bound to, with nothing pasted.

The verifier is not new. oaStrainFromOidc has always checked the token through Google's tokeninfo, pinned the audience to MCP_PUBLIC_URL, required a .iam.gserviceaccount.com subject and mapped it to an active strain. It was unreachable in practice for one reason: nothing ever wrote the field it reads. This release adds the writer and teaches the resolver to tell the two bearer kinds apart.

  • /api/strains/provision takes an optional sa_email — shape-guarded to a service-account address, lower-cased to match tokeninfo, empty string clears it.
  • oaBearerRole splits into oaBearerIdentity returning {role, oidc}. An OAuth connector token is oidc:false; a Google-attested token is oidc:true; only oidc:true skips the paste. An account-level connector bearer still gets exactly the denial it got before, because one bearer shared across every chat must never resolve a keyless call to a live toolset.

It grants nothing new

The resolved strain is admitted through the same buildMcpServerAdmitted path as any pasted key, tool_classes and all. A presented key still wins, and a presented key that is wrong still fails closed rather than falling back — that is [FAIL-CLOSED-ON-BAD-KEY] and it is untouched. What changed is only what counts as proof: possession of a minted key for a person; possession of an identity IAM already vouches for, for a machine.

Proven as an A/B, on both lanes, before any traffic moved

The same token and the same call, sent to the new revision and to the one it replaces:

new revision → ROLE: <the bound strain>
old revision → DENIED: This chat has not established an identity yet.

A tagged Cloud Run revision keeps the previous code serving on its own URL, so the control is one request away and the claim is a measurement rather than an inference. Run on dev, then again on production.

What it is for

An agent deployed on a managed runtime — Google's Agent Runtime, Cloud Run, a build step — runs as a service account. Bind that account to a strain and the agent is that strain: it holds exactly the tool classes the strain holds, it is attributed as the strain in the journal, and there is no key in its image, its config, or its chat transcript to leak or rotate. The first agent built on this release answers whoami as its strain in three seconds with nothing pasted anywhere.

Verification

Cut deterministically: gen.py run twice into separate directories, diff -r reports zero. The same image digest served the dev lane before it served production. Bus gate 0 (negative control 4/4); passkey gate 0 (negative control 7/7, four asserted allowances). check-apis, check-python-names, check-no-embedded-assets and bash -n on all three scripts green against the emitted tree, not the source.

Apache-2.0. Your project, your bill, your key.

v12.7 — Gemini 3.8 Flash by default, a Gemini loop configured for 3.x, and tools that say what they are

Choose a tag to compare

@paracoding-ai paracoding-ai released this 06 Sep 00:09

103 files. Nine paths move; the other 94 are byte identical to 12.6. MANIFEST.txt lists every file and its digest. Cut deterministically from source commit cc217553; the same image digest served the dev lane at 100% before it served production.

The console defaults to gemini-3.8-flash

Verified the way 3.7 was, not inferred from an announcement: Model Garden answers GA for the id (released 2026-09-02), Google's model list marks it New, Stable, and the first production turn on this release resolved to gemini-3.8-flash over Vertex at global and answered in 12 seconds with 55% of its input served from the implicit cache. The Pro button stays on gemini-3.1-pro-preview: Model Garden also answers for a "gemini-3.5-pro" that appears on none of Google's published model lists, pricing pages or model cards, and one endpoint the rest of the vendor's surface contradicts is a registration, not a launch. CHAT_API_GPRO moves the slot without a rebuild.

The Gemini loop knows it is talking to a 3.x model

CHAT_GEMINI_THINKING (low | medium | high) is the Gemini twin of CHAT_EFFORT; unset — the shipped state — sends no thinking field and leaves each model on its own default. minimal is refused by 3.7/3.8 Flash and therefore by the console, before any spend. Thinking tokens are billed as output and are now counted as output in /api/usage; they were not before. The tool loop returns the model's parts verbatim, which carries the thought signature Gemini 3.x requires on the next round. A multi-part text answer is joined instead of truncated. The model configuration page says all of this, including the 3.x Flash introductory price that doubles on 2027-01-01.

Every tool carries MCP annotations

Derived from the one tool-class table the server already keeps: the 28 read-only tools declare readOnlyHint; writes that remove or cancel something, and every tool that reaches the executor or Google Cloud, declare destructiveHint. Measured on Gemini Enterprise connected to this server as a third harness: without annotations it put a review card in front of every call, whoami included; with them, reads run and actions ask. Clients that ignore the field see no change.

Proven before publishing

gen.py cut twice, diff -r zero. Bus gate 0 (negative control 4/4). Passkey gate 0 (negative control 7/7, four asserted allowances). Route audit 74 / 58 guarded / 16 public, no route changed. Dev lane on this digest: console 51 routes, mcp 28, clean boot. Then production, same digest, and a live Gemini 3.8 Flash turn.

Apache-2.0. Your project, your bill, your key.

v12.6 — the passkey is gone, the installer never stops, and the product stops lying about itself

Choose a tag to compare

@paracoding-ai paracoding-ai released this 05 Sep 05:31

103 files. 27 paths move, one is removed (control-plane/src/locked.html); the other 75 are byte identical to 12.5. MANIFEST.txt lists every file and its digest. Cut deterministically from source commit bf9255e5; the same image digest served the dev lane at 100% before it served production.

WebAuthn is deleted, not disabled

IAP at the front door, a Google identity on the approver allow-list, then a session cookie with a TTL. No passkey on top of that. Fourteen /api/webauthn/* routes, ten helpers, five WA_RP_* constants, PC_REQUIRE_PASSKEY and every branch it guarded, the @simplewebauthn dependency on server and console, and locked.html are gone in one commit. There is no switch that re-arms it. /api/webauthn/status becomes /api/auth/status. Route baseline 88 → 74 registered, 17 → 16 public; the audit passes against it. The auth-path diagram is re-rendered.

What stays, named so nobody mistakes it for a leftover: the gate_session cookie under WA_SESSION_SECRET (not rotated — live sessions survive the upgrade), and the executor's own gate-exec/pcwebauthn.py behind PC_REQUIRE_ASSERTION=0, which is a separate service with a separate deploy and gets its own release. install.sh therefore still creates the webauthn-creds secret the executor reads by name. The release build now greps the emitted tree for the whole passkey vocabulary and refuses to build on a hit, with those executor-side files as asserted allowances that must still match.

The installer prompts for nothing

Step 6d/10 stopped on stdin asking for more allowed Google accounts. It now seeds the account running the install and moves on. --approver-emails a@b,c@d and PC_APPROVER_EMAILS cover the non-default case (validated, de-duplicated, flag beats env, in --help); the Settings page adds and removes accounts afterwards. Note: re-running install.sh with --approver-emails replaces the list; it does not merge with accounts added in Settings. The only stdin read left is the opt-in --plan confirmation.

The product tells the truth about itself

Fifteen strings sent to a model or returned as tool results said a human approves each staged job "with their passkey", or that a job "sits on the gate until a human approves it". Under PC_AUTO_APPROVE=1 a staged job is KMS-signed and executed in the same call; under =0 it sits at pending where nothing comes to approve it. All fifteen now say what happens, the journal line for a staged job names the posture instead of an approver, a refused lockout-class job relays the executor's rule ids and reasons instead of rule(s): unnamed, and two runbooks that claimed protections the system does not have now say what is true and what actually bounds it.

put_file cannot write a corrupt binary

Models corrupt long base64 in tool arguments (measured: one wrong character at 10,011; a dropped one at 60,000; a 64KB chunk that degenerated into filler and landed, in a store with no delete). The decoded size is now bounded at 40,000 bytes, the refusal names the byte-exact route (POST /git/blob then git_propose uploaded{blob_oid, sha256}), an optional sha256 over the decoded bytes is checked, and the payload is re-encoded and compared.

One temporary measurement, labelled as such in the source

One log line per POST /mcp records whether an Mcp-Session-Id header was present, a 12-hex hash of it, and whether it changed — never the value, never the session key. Binding a chat on that header unmeasured would trade a paste-loss bug for a cross-chat identity leak.

Proven before publishing

gen.py cut twice, diff -r zero. Bus gate 0 (negative control 4/4). Passkey gate 0 (negative control 7/7, four asserted allowances). Route audit 74 / 58 guarded / 16 public. Dev lane on this digest: console 51 routes, mcp 28, /api/auth/status 200, /api/webauthn/* 404, clean boot. Then production, same digest.

Apache-2.0. Your project, your bill, your key.

v12.5 — nothing runs on a timer, and now nothing says otherwise

Choose a tag to compare

@paracoding-ai paracoding-ai released this 31 Aug 13:40

Full release tree at commit fb40b85fa26202714898c62f8b4160653d3dc8f2 of the source repository, published by the product's own gh_commit reading each blob straight out of its own git store. MANIFEST.txt lists every file and its digest. 104 files. Fourteen paths move; the other 90 are byte identical to 12.4.

12.0 deleted the runners. What survived was the vocabulary — and in four places the vocabulary was making a promise the code could not keep.

The console told you your work was somewhere it was not

Every strain's status light in the Flow Hood rendered queued work as "3 on the bus". There is no bus. It now reads "3 queued", which is what that number has always actually been: work_items with status pending.

Two system prompts were false rather than stale

Every strain in every install was sent them.

  • The operator digest printed BUS: gemini-only. Sweeper tick PAUSED — stalled work parks, it does not escalate to Claude. There is no sweeper to pause, and paused invites an agent to ask for it to be resumed.
  • The per-strain digest printed Your twin runs your items as you, on GCP billing. Nothing runs anyone's items.

Both now say DISPATCH, and both say plainly that nothing runs unattended. The law constant beside them said BUS LAW: there is no bus; a law that has to name the thing it denies keeps the thing alive in the reader's head, so it now reads DISPATCH LAW: nothing in this install runs unattended.

The cost page warned you about unattended spend from a component that does not exist

wiki/pages/model-config.md, under Cost: "Chat is per-turn and interactive. The work-item bus is not: it runs queued items without you watching. Turn it on knowing that."

That is the page a person reads before deciding what to switch on. It now says the true thing, which is also the reassuring one: chat is the only thing that spends, and there is no unattended spend to turn on.

A shipped API field named the deleted mechanism

/api/flow returned bus per agent and per feed row, derived from journal rows only the runners ever wrote — so on any install it is permanently "unknown". Renamed to model, which is what it holds. Verified safe before renaming, not after: a grep for \.bus\b|\bbus: across all 55 files of control-plane/ returns eight hits and every one is in index.ts. No consumer changes.

The one thing deliberately not removed

The cloudscheduler API. install.sh enables it and step 2/10 grants admin on it, while the rationale comment beside it named two runner deploy scripts that no longer exist — which reads as an instruction to drop the API. Dropping it breaks installs. The comment now says that in as many words, alongside the two facts that settle it: install.sh creates no scheduler job itself, and a gate executor driven by a scheduled tick needs the API to exist.

How this is enforced from here

The release build now cuts the tree and greps the emitted bytes — what a downloader actually gets — for the whole vocabulary, and refuses to build on a single hit. It proves the pattern can still match before it judges the tree (a four-line negative control), and its one allowance — an upstream MCP SDK option bag quoted verbatim in a comment — asserts it still matches, so a dead allowance fails the build rather than sitting there as a standing hole.

This is not decoration. A grep of the source reported index.ts clean while it carried twenty hits, because the pattern asked for "event bus" and not "the bus". Four of the ten the gate caught live in files that do not exist in the source repository at all — README's paragraphs are emitted from string literals in the generator. The emitted tree is the only honest subject.

Still in the tree, and this release does not pretend otherwise

The fifteen /api/webauthn/* routes and control-plane/src/locked.html. Removing them is a separate commit for a real reason: gen.py's REQUIRED list names locked.html and refuses the cut without it, and a second gate cross-checks every pcHtml() argument against the emitted tree, so the file, its loader and the manifest entry have to move together. The passkey requirement was already gone in 12.4; what remains is dead surface, not a live gate.

Checks

Cut deterministically — gen.py run twice into separate directories, diff -r reports zero. Route audit: 88 live routes, none unrecorded, none vanished, 17 known public, no new unguarded route. Blob audit clean over 35 files. check-apis, check-python-names and check-no-embedded-assets all green against the emitted tree, not the source.

Apache-2.0. Your project, your bill, your key.

v12.4 -- you could not create, clone or delete a strain on any install but ours

Choose a tag to compare

@paracoding-ai paracoding-ai released this 31 Aug 03:13

v12.4 — you could not create, clone or delete a strain on any install but ours

Full release tree at commit 5966424ee0e22cd2617ac4df7074925cfa7c2254 of the source repository,
published by the product's own gh_commit reading each blob straight out of its own git store.
MANIFEST.txt lists every file and its digest. 104 files. Eight paths move; the other 96 are
byte identical to 12.3.

IF YOU INSTALLED 12.3, THREE BUTTONS DID NOT WORK. Create a strain, subculture one, delete one
— each answered with a popup reading "Passkey: no registered passkey" on a product that does
not ship passkeys, does not ask you to enrol one, and cannot enrol one because install.sh
never sets WA_BOOTSTRAP_SECRET. There was no way around it from the console.

THE DEFECT WAS ONE MISSING CONDITION, IN THREE PLACES. /api/strain/create, /delete and
/subculture each opened with if (!waElevatedOk(req)), and waElevatedOk never consulted
PC_REQUIRE_PASSKEY. install.sh ships PC_REQUIRE_PASSKEY=0. The approval path in the same file
had it right — if (PC_REQUIRE_PASSKEY && !waElevatedForJob(...)) — so the correct pattern
was sitting eleven hundred lines away from the three call sites that lacked it.

WHY FOUR RELEASES SHIPPED WITH IT. This fleet's own production console carries a WebAuthn
credential in Firestore from an era when the gate was armed. There, the pre-flight returns a
challenge, Face ID appears, and the buttons work. A from-zero install has no such row, so the
pre-flight 400s and the handler returns before it ever reaches /api/strain/*. Every release
was exercised on the one install in the world that could not reproduce the bug. The operator
found it in twenty minutes by installing the published zip into a clean project, which is the
only test that was ever going to find it.

WHAT CHANGED. The three guards are gone, and so is the approval conditional they should have
matched: with PC_REQUIRE_PASSKEY=0 on every shipped install, that check has never once fired,
and a check that cannot fire is not a control. Nothing is loosened that was ever tight. The
console's three client-side handshakes go with them, because the server fix alone would not
have helped — the browser gave up before it called the server. So does a poller that hit
/api/webauthn/pending every fifteen seconds behind an empty catch.

IAP IS THE DOOR, AND NOW SAYS SO. waSessionOk's IAP branch was wrapped in
if (!PC_REQUIRE_PASSKEY), a condition true on every install this installer has ever
produced. It is now unconditional — the code finally describes the posture the product
actually ships. The fail-closed audience guard is untouched: no PC_IAP_AUD, no admission, so
an assertion minted for some other IAP-protected app still cannot satisfy this gate.
waMakeSession stops stamping pk and waSessionOk stops requiring it, in the same commit,
because doing one without the other is a total lockout in one direction or the other.
WA_SESSION_SECRET is untouched, so sessions live across the upgrade.

AND A COMMENT THAT PROMISED SOMETHING THAT NO LONGER EXISTS. Every staged destroy-class GCP
job was stamped "# DANGER destroy-class GCP call (METHOD) — Face ID required" into its own
command header, and into the journal. No Face ID had been required on that path for months.
An operator reading the audit trail was being told a tap happened that never did.

THE PASSKEY ROUTES ARE STILL IN THE TREE AND THIS RELEASE DOES NOT PRETEND OTHERWISE.
Removing the fifteen /api/webauthn/* routes and locked.html is the next commit, not this one:
gen.py's REQUIRED list names control-plane/src/locked.html and refuses the cut if it is absent,
and a second gate cross-checks every pcHtml() argument against the emitted tree, so the file,
its loader and the manifest entry have to move together. Shipping the fix tonight was worth
more than shipping the tidy-up with it.

Cut deterministically: gen.py run twice into separate directories, diff -r reports zero.

Apache-2.0. Your project, your bill, your key.

v12.3 -- it is an MCP connector for Google Cloud, and it is not Claude-only

Choose a tag to compare

@paracoding-ai paracoding-ai released this 30 Aug 18:25

v12.3 — it is an MCP connector for Google Cloud, and it is not Claude-only

Full release tree at commit 9d90a3bb9afa709e23beda285bd930c05f052392 of the source repository,
published by the product's own gh_commit reading each blob straight out of its own git store.
MANIFEST.txt lists every file and its digest. 104 files. Nine paths move, plus one screenshot.

THE README NOW LEADS WITH THE PRODUCT. It opened by calling this "an agent platform"; it opens
now with what it actually is — an MCP connector that gives a chat hands on your own Google
Cloud project. Point any MCP client at it, ask for software, and it builds the container,
deploys it to Cloud Run in your project, and reports the HTTP code it got when it checked.
The client-agnostic property was buried in a bullet that called this "effectively a
Claude-to-GCP connector". It is now a headline, and it is a measured claim rather than an
aspiration: the session key travels as a TOOL ARGUMENT rather than a client-specific header,
and this exact install has now been driven end to end from two different vendors' clients.

GROK WORKS, AND IT WAS QUICK. Connect the MCP connector, paste a session key, and it drives
the same fleet: same GCP project, same tool surface, same internal git store. That is the
whole integration.

NEW SECTION — TWO MODELS, TWO PLANS, ONE PROJECT. How this fleet is actually run, written up
because it is the reason client-agnosticism earns a headline instead of a footnote.

Claude Max is the architect: design, the calls that are expensive to get wrong, release
cuts, and the security review — Opus 5 on high effort reading the diff and asking what
breaks.

Grok is the engineering loop: long mechanical passes over a large tree, inventories,
refactors, hunting every call site of a thing being removed. It is stellar at this and
patient in a way that is expensive to buy from a reasoning model.

Claude in Chrome is the bridge: Cowork drives a browser tab logged into grok.com, so the
architect hands work to the engineer and reads the result back with no human copy-pasting
between two windows.

Two sets of eyes on every line, and they are not the same eyes — a disagreement between two
vendors' models is the one signal a single model cannot give you at any price, and more than
one defect in this release was found because the second model refused to agree with the first.
And the economics are the point: reasoning tokens are the expensive ones, so spend them on
architecture and review while a second plan absorbs the grind.

WHAT MAKES IT WORK IS THAT BOTH CHATS AUTHORISE INTO THE SAME PLACE. Neither model holds a
Google credential. Each presents a session key to the same OAuth-connected MCP connector, the
control plane resolves it to a role server-side, and from there they share the same GCP
project, the same MCP tools, the same internal git store with its objects sealed under X-Wing
(ML-KEM-768 + X25519), the same data lake and the same journal. Work one model does is visible
to the other because it was never in a chat window to begin with. The Flow Hood is the control
panel for that — where strains are created and managed, and where each harness's session paste
comes from — so putting a second model on the same fleet is a paste, not an integration.

THE WORD "BUS" IS RETIRED, IN ALL THREE PLACES IT SURVIVED. The runners are gone — 12.0 deleted
all 22 files under runner/ and the autonomous loop with them — but README, FEATURES and SECURITY
still described "model buses". What actually survives is models.fleet_mode, which governs ONE
thing: the console's own built-in chat. The connector itself makes no model call at all; your
client is the model. All three documents now say that, and each says the word is retired and
why, so nobody goes looking for a dispatcher that does not exist.

ALSO IN THIS RELEASE: gh_tag and gh_release. This repository advertised v10.5 as its Latest
release while main carried v12.2 — three releases behind, for eleven days — because gh_commit
could publish the whole tree and could not name the version it had just published. Verified
live in the deployed revision, from the server's own log rather than a client's cache:
[ghtools] registered 12 tools, up from 10.

Apache-2.0. Your project, your bill, your key.

v12.2 -- the reporting law goes in the file every agent is handed

Choose a tag to compare

@paracoding-ai paracoding-ai released this 30 Aug 18:24

v12.2 — the reporting law goes in the file every agent is handed before it does anything

Full release tree at commit e4ad310120bbd76ed8643367001409a0c8cd95f2 of the source repository,
published by the product's own gh_commit reading each blob straight out of its own git store.
MANIFEST.txt lists every file and its digest. 104 files. Nine paths move; the other 95 are byte
identical to 45c3aa6. No code changes — this is a prose release, and in this product the prose
IS the interface between an operator and every agent they run.

THE RULE THAT KEPT NOT STICKING. fleet/BOOTSTRAP.md is delivered by whoami at the top of every
session, before an agent does anything; the file says of itself that a rule an agent must
remember to look up is a discipline mechanism, and discipline mechanisms fail silently. The
reporting rule had been living everywhere EXCEPT there — in individual strain charters, as
prose, near the end — and it was violated for two months by agents that had it written down.
Section 8 now carries it, each part with the measured cost that earned it, because a rule with
no evidence behind it reads as a style preference:

THREE LINES BEATS THREE PAGES, and a long report is usually a failure to decide — you did the
work, you know the answer, and you handed over the raw material instead of the conclusion.

DECIDE IT YOURSELF if it is reversible, cheap and in your lane, and report it in one line.
Handing your own job back as a question is the failure, not the caution.

WHEN THE DECISION IS GENUINELY THEIRS, lettered options with costs and ONE recommendation.
Never a wall of prose ending in a question.

DO NOT ASK A HUMAN TO DO SOMETHING BEFORE YOU HAVE TRIED IT YOURSELF. A refusal you have hit
is a fact; a refusal you assumed is a guess, and ONE refused tool is not a refused category.
If a human must run something, hand over the whole command with nothing left to fill in.

SECTION 1 GAINS A SCRATCHPAD THAT SURVIVES YOUR OWN CONTEXT. Write the plan to
agents//current-task.md before a long run — branch, intended commits, done-so-far —
and never the session key, including never into a client-side prompt store, which is the same
exposure as writing it to the lake. The measured reason: a host compaction replaced the key in
its own summary with the literal word "placeholder" because it looked like a secret to strip,
and every call after that was DENIED with no way back but a human re-paste.

APIS.TXT NO LONGER TELLS YOU TO DELETE THE API THE INSTALLER CANNOT BUILD WITHOUT.
compute.googleapis.com was commented "the workstation VM and its network". 12.x ships no
workstation, so that sentence is an instruction to remove the line — and removing it breaks
every install. Verified in this tree, three live reasons, all now written down: Google creates
-compute@developer.gserviceaccount.com only once the API is on and CLOUD BUILD RUNS AS
THAT ACCOUNT (install.sh waits two minutes for it and dies); the post-quantum TLS controls read
and PATCH compute/v1 sslPolicies and aggregated targetHttpsProxies; and blessed direct GETs are
allowed under compute/v1. This is what a stale comment costs when the thing it describes is
deleted and the comment is not.

Checks run against the emitted tree, not the source: check-apis.py OK (batch=20 both sides,
"the installer enables exactly what fleet/apis.txt declares"), check-python-names.py OK over 31
files, check-no-embedded-assets.py OK over 197, and check-release-tree.py reporting 104 emitted
files 104 verified byte for byte, 0 unchecked, MANIFEST covering 103 with every hash matching,
and source provenance clean at HEAD. Cut twice into separate directories: diff -r zero.

Apache-2.0. Your project, your bill, your key.

v10.5 — five auth fixes, no object versioning on encrypted buckets, and a settings drawer you can read on the light theme

Choose a tag to compare

@paracoding-ai paracoding-ai released this 19 Aug 11:59

Full release tree at commit cbe9a8b8 of the source repository, published by the product's own gh_commit reading each blob straight out of its own git store. MANIFEST.txt lists every file and its digest. 125 files, 27 of them executable.

Five authentication and authorisation fixes

Every one was re-measured against current code before it was touched, because the audit that found them was taken three releases earlier and a finding from a stale tree is a guess.

The agent-card route served a HIDDEN strain's full card to an anonymous caller. The roster listing already filtered hidden strains; this route did not, which made hidden a listing preference rather than an access control. It now answers 404 with the same body as an absent or inactive strain, so a prober cannot tell "hidden" from "does not exist" by response shape.

The console session check called the IAP-identity helper without the audience guard its two sibling call sites both apply. install.sh sets the audience variable, so this is defence in depth for a hand-rolled deploy rather than a live hole — but it was the one site that dropped a check its siblings keep, on the function guarding the most.

The queue-claim route filtered work items by a client-supplied role while recording the server-resolved caller as the claimant, and never compared the two. Any holder of a valid agent token could claim another role's queued work. It now refuses with 403 rather than silently substituting the resolved identity, so a mismatch is visible instead of quietly reassigned.

Session enforcement defaulted OFF in code and ON in the installer. Inverting the code default is a no-op for every installed system; it closes the gap on a hand-rolled deploy that omitted the variable, where "unset" silently meant "never deny".

The default role for an unbound OAuth connector was seeded with no tool classes, which this system resolves as EVERY class — including privileged staging — for an identity nobody has bound to a human yet. New installs now seed it read-only. Note for existing installs: the seeder returns early unless the strain collection is empty, so this does NOT retro-fix a running fleet. Check that role yourself.

Unchanged, and now commented rather than quietly load-bearing: the wildcard CORS header on both surfaces is safe ONLY because credentials are never allowed, so a browser withholds the session and IAP cookies cross-origin. Adding a credentials flag to that middleware would make every cookie-authenticated console route readable cross-origin.

No object versioning on the buckets this installs

install.sh now asserts --no-versioning on the data lake, the git object store and the exec-records bucket, alongside public access prevention and uniform bucket-level access.

Every object this system writes to those buckets is encrypted at rest, so a retained noncurrent generation is ciphertext, not a backup: unreadable once the vault epoch moves, silently holding bytes that were meant to be replaced, and billed for the whole time it sits there. It buys least of all on the git object store, where blobs are content-addressed — a write either creates a name that did not exist, or rewrites a name with byte-identical content, so there is no earlier version to keep. History is in the objects. Git is the rollback.

It is asserted, not merely omitted, and on an existing install that is the whole change: dropping the flag would only stop the installer turning versioning ON and would do nothing to a bucket that already has it. Suspending it is not destructive — generations already retained stay retained.

Two refusal messages in the work runner told you to recover a damaged encrypted object "from object versioning", one of them printing the gcloud command to do it with. On a bucket without versions that command returns the single live object and you conclude the restore worked. Both now say there is nothing to restore from, and to report it and stop.

The settings drawer is legible on the light theme

The drawer paints its own surface in colour literals but reads the theme's ink token for text, so the light palette added in 10.3 rendered near-black text on a near-black drawer. It is the drawer you open to CHANGE the theme, so the one control that gets you back out of a theme you did not like was the one that theme broke. The effective dark set is now pinned on that element: a custom property on a nearer ancestor wins by inheritance, so it holds without !important and without touching any palette.

The plugin manifest no longer claims a gate that is not in the path

agent-plugin/plugin.json said "No privileged action runs without a WebAuthn approval on your own device." install.sh ships PC_AUTO_APPROVE=1, PC_GUARDRAILS=0 and PC_REQUIRE_PASSKEY=0, so a job an agent stages is signed and executed in the same call. SECURITY.md was already honest about all three; the plugin manifest — the file you read BEFORE installing anything — was the one contradicting it. It now says what happens and names the one setting that stops it.


Apache-2.0. Your project, your bill, your key.

v10.4

Choose a tag to compare

@paracoding-ai paracoding-ai released this 18 Aug 19:46

Install or upgrade with ./install.sh — it adopts an existing install and refuses a downgrade. MANIFEST.txt lists every file and its digest. 125 files, 27 executable.

The chat's transport is now visible, and it reaches models it could not before

Vertex is the default transport for both Claude and Gemini and needs no API key. That was already true and almost nothing said so — Settings showed an empty key box, which reads as "unconfigured, paste a key". Each provider now prints what actually resolved: transport, region, model, and when something is wrong, the blocker and the one setting that moves you to the other transport. Read the line, not the box.

Claude gained the global-endpoint handling Gemini has had for months. Some published models are served only on the global Vertex endpoint, and asking a regional host for one does not 404 — it answers:

HTTP 429 Quota exceeded ... base model: anthropic-claude-opus-5

which reads as a quota problem, and the quota tables agree, because the per-base-model metric enumerates regional buckets only and has no row for a model that is not served regionally. The model is fine, the quota is fine, the endpoint is wrong. Both providers now detect this and override the configured region, logging the model that caused it. If you see a 429 naming a base model, check the endpoint before you file a quota request.

The opus-5 floor stopped eating newer models. It force-upgraded anything matching the prefix claude-opus-4 — right when that meant 4.0 and 4.1, wrong once the family gained 4-5, 4-6 and 4-8, which postdate the floor and are often the models a project actually has quota for. Genuinely stale ids still upgrade; newer ones are yours to pin with CHAT_API_OPUS.

A light theme, and chrome you can rebrand without forking

Six palettes: purple (still the default), green, orange, blue, dark, and light. The light one is 24 token values and no rule changes — every colour the chrome uses reads a CSS custom property, so a palette is data rather than a patch.

Two things worth knowing before you change any of it:

  • The hue is a token; the alpha stays at the call site. Colours are written rgba(var(--ink-rgb),.28) rather than collapsed into flat tokens, because the accent appears at five different alphas, recessed surfaces at eight, and the ink at twenty-one. Collapsing each family to one token is the obvious refactor and it silently restyles panel depth and border weight everywhere while looking like a cleanup.
    • The logo is data too. The shipped mark is a 96×96 square that the stock filter chain paints gold, so the defaults crop to a circle and recolour — fine for that asset, ruinous for a wide lockup, which gets cropped to its first two letters. --logo-filter, --logo-radius, --logo-fit-*, --logo-w-*, --logo-h-*, --logo-maxw and --logo-shadow-* carry the treatment, so a finished asset is a list of overrides instead of an edit.
      Mushroom mode is a switch rather than something to delete: window.PC_BRAND.whimsy = false turns it off and hides its four toggles. It overrides the stored preference rather than seeding a default, because anyone who has ever clicked the toggle carries pc_mush=1, and moving only the default would leave it on for exactly the people who use it.

Known limit, stated rather than implied: chrome strings are still authored in harness.html, not read from data. Changing them means carrying a patch you re-apply on upgrade. Colours and logo treatment are data; text is not, yet.

One self-test was red on a working console

install.sh step 10/10 grepped /harness for a page title this installer never serves: it ships PC_REQUIRE_PASSKEY=0, which serves login.html, and that string is only in locked.html. The check now asserts pc-locked-stage, a structural marker present in both documents — so it says "an anonymous caller reaches the locked stage" without asserting which posture is deployed, and without breaking when somebody renames a brand word.


Apache-2.0. Your project, your bill, your key.