Repository navigation
Releases: medici-finance/assay
Release list
v1.0.39
Highlights
Added
- A new stdlib-only Go module,
forgeread, is the importable fact reader: one narrowReaderAPI for the open issues of an explicit repository set, with the three-state result (read, known empty, unavailable) made a type. A repository appears exactly once, as data or as unavailable; a provider that keeps paginating past the walk ceiling is reported as truncated rather than as a short list; an envelope with an unknown schema is refused. Offline and frozen-result readers live in the root package, which imports no HTTP client, no process launch and no subpackage. The Go API and the JSON envelope are versioned separately. - Draft unified non-CI architecture, shared context-graph design, future-aware authoring delivery scope and verifier-first migration proposal; implementation and activation remain separate work.
- Every exit the intake desk takes now lands one structured
intake-exit-v1record:scanloop runappends amechanicalrecord per landed exit, the newscanloop landverb records ajudgmentexit for a parked item, andstatusgen --intake-exits --jsonexports the intake register's stamped entries with a stamped/unstamped/unmapped summary. Intake entries gain an optionaltriaged/triaged-by/triager-tierstamp, linted against a closed role set so no person's login can enter it. The record file holds one exit per inbound event (item plus itsopenedtime): the drain's EMITTED report prints thescanloop land --openedcommand for each parked item, so an update parked after the same issue's earlier new-issue exit is recordable. A refused or unwritable record never costs an exit its auditlandline or another batch member its exit, andlandrefuses a session that did not declare its loop. - Extend the proposal with resumable workspace retirement, bounded Git maintenance, durable feedback reevaluation, observable loop guards, governed context updates and sustained-work qualification.
- Recording costs no extra forge reads: the Forge decorator records from the
ChecksAtHeadand rollup results the tools already fetch, and a recorder failure never changes what the read returns. - The desk tools now keep a CI check history: one
ci-check-v1line per finished check run or terminal commit status they read (repo, head SHA, PR, check name, attempt, conclusion, duration), so flake rate and check duration can be measured later. Check output, URLs and who posted a check are never recorded. - The history lives in the local desk state directory as
ci-checks.jsonl, rotated daily, never committed and never deleted by the tools;deskkit.LoadCIChecksreads it back de-duplicated. deskread issuesmarks a truncated repository with an optionalclassfield on its unavailable entry (additive; the envelope schema number is unchanged).
Fixed
- The forge-neutral/33 brief's Verify table can now be witnessed after merge: rows 17 and 19 name the implementation commit and the brief's authoring base instead of a merge-base that resolves to HEAD on merged main, row 19 asserts on the
consumerssummary line rather than exit 0 alone, the unwitnessable GitLab live row is withdrawn into a note, row 4's name counts are corrected, and the origin of the 25-page GitLab ceilings is stated beside the constants. - The scan-override audit identity (
deskkit.OverrideIdentity) now resolves to the accountable roster login instead of the raw OS account name: the newASSAY_OS_USER_LOGIN_MAProster key (osuser:loginentries, an extension key — a malformed or off-roster value empties only that map, never the roster) binds a machine account to a trusted-human login, with the human-name map and the trusted-login set as further resolution steps. Resolved rows carry the observed OS account as<login>(os:<account>); an OS account nothing resolves still produces an audit row under a clearly-markedos:<username>fallback — the identity label degrades, the audit write never blocks. muharparallel sweeps (-j> 1) no longer discard every run as "baseline RED" when the swept module'sgo.modcarries a relativereplacepointing outside the copied tree (for exampletools/desk'sreplace … => ../../forgeread). Each worker's isolated copy now re-points those filesystem replacements to their absolute location in the source tree; in-tree, versioned and absolute replacements, and fixturego.modfiles undertestdata/, are copied unchanged. Mutations still land only in the copy.tools/desktests no longer touch the operator's live cell Go-cache. A test run started from a cell shell inheritedASSAY_GO_CACHE_POLICYand took the live cache's exclusive lock throughdeskdispatch's storage admission andcellprocess's launch custody, so production dispatch on the same machine returned storage-deferred while the tests ran (and the tests failed while production held it). Every package whose code links the managed cache now clears the cell's storage-admission variables in itsTestMain(new test-support packageinternal/cachequiet), and a static guard fails any new cache-linking package that does not.
Changed
- Make the parser-bypassing admission regression use a valid configuration directory and assert the intended refusal, so an earlier fixture error cannot mask a removed domain check. Keep its policy fixture inside the Go module so isolated mutation workers execute the admission checks instead of skipping them.
- Preserve current main’s
deskdcustody behavior through the new command tree: defer roster reads until the selected cell is entered, including no roster echo on an earlier selector or attendance refusal. - The container build files copy
forgeread/beside the modules that resolve it. - The hourly board-reconcile job is live. The statusgen board workflow now runs
reconcileon an hourly schedule in this repository only: it computes generated stream-README changes under a read-only token and publishes them as one draft pull request onboard/reconcile, minting the board-writer App token only in the publish step. Its job tests now run in the board workflow's PR lint job. - The open-issue paging and mapping for GitHub and GitLab now live once, in
forgeread/adapters, and desk-tools' forge methods delegate to it over their own authenticated transport. Credential resolution and host and repository validation stay in desk-tools; the delegated reader refuses any repository other than the one a credential was admitted for before it builds a request. Behaviour is unchanged. - The shared fact reader's CI controls are live.
forge-surface-controlnow runs onforgeread/**changes and executes the module's tests, the ambient-token rule overforgeread/, the lower-layer scope check and statusgen's import-path guard. The statusgen board workflow now triggers onforgeread/**, and its corroboration guard archives the siblingforgeread/module into the base tree, so a base that already carries the module builds against statusgen's relativereplace. cellctlnow parses its command line with Cobra and resolves its per-run settings through the shared desk adapter. Every verb, flag and precedence rule is preserved, and every refusal is preserved except where the recorded decisionDR-cellctl-cobranames the change (as measured against the pre-migration binary over the lines the compatibility page lists): a line of a shape the old parser refused (a flag or a lone-before the verb or ahead of a verb's positionals,--cells-rootanywhere but first or in another spelling, the single-dash spelling of a long flag outsidescratch) is refused again, exit 3, before anything runs, and a relative--cells-rootis refused first on every line, as before.cadence,cacheandcommsstill compare--confirm-stoppedas an exact word in its place,checkanddeskdstill read the words after their cell by position, the first--kindon adesk,uporshowline ahead of a bare--is still the kind (also in another flag's value position, and refused there when it has no value or an unknown one) while a later--kindis skipped unread, andnewstill prints its usage for-hor--helpanywhere on its line and takes a value flag left last with no value as empty.--help,<verb> --helpand--versionread no cell or credential (and the roster only to refuse a relative--cells-root),--flag=valueand--are accepted, and a parse failure carries Cobra's wording with the usage exit code (3, includingscratch, which used 2, anddesk, which used 1 for a flag-shaped word in its cell's place). A false help flag (--help=false, and-h=falseonscratch) is read as no help and the line runs, under entries 4 and 9. The differences are listed indocs/cellctl-cli-compat.mdagainstDR-cellctl-cobra, and the generated command list isdocs/cellctl-cli-reference.md. The hidden shell-completion entrypoints are refused like any unknown command.forge-ban.shcountsforgeread/as its own tree (forgeread sites: N) and exits non-zero when it is above zero; the desk and statusgen columns stay advisory.statusgen --scan-issuesnow derivesgate: humanon a new issue placeholder when the issue already carries therisk:highlabel (risk:medandrisk:lowdo not gate). The intake-desk skill's scored-triage section says the gate is derived once at first write, and what a triage session does when the placeholder came first.- statusgen reads the shared types: its default forge reader is the library's offline reader, and its online reads still run the
deskreadverb.
assay release v1.0.39 - statusgen + qualgen + desk-tools + drainloop.
Binaries (pin per-platform by sha256 in .assay-versions):
- statusgen-
- qualgen-
- desk-tools-.tar.gz
Source components (cloned or imported at this tag; pin the umbrella , never a per-component tag):
- github.com/medici-finance/assay/drainloop
desk-tools image (built sepa...
v1.0.38
Highlights
Added
- Maintenance requires native Git hook inventory and holds repositories with listed relevant entries or unverifiable configuration, preserving existing hooks. Unrelated events remain usable.
deskwt maintainplans and applies bounded offline ref packing and commit-graph updates, with a shared-repository lock, attempt cadence, and before/after inventory. Object reclamation remains held until borrowed-object ownership can be established.deskwt prunenow garbage-collects stale local branches in the same sweep (Step C): a branch is collected only when its content is provably onrefs/remotes/origin/main— ancestry-merged (which covers fresh, never-used handles) or cherry-clean (every unique commit's patch-id already in the mainline, the squash-merge case) — and nevermain/master, a branch checked out in any worktree, or anything whose proof cannot be completed. Deletes are compare-and-delete against the proven sha (no force verb), live sweeps print per-class counts only, and--no-branches/--branches-onlysplit the pass from the worktree arms for cadence control. (#2504)deskwt prunenow pressure-checks every marked per-cell Go cache root under the cells root on each sweep — the cadence backstop for the managed caches that were previously checked only at launch and dispatch. Enforcement keeps the package's own semantics (reclaim only over the byte budget or under the free-space floor, never under a live consumer lease), a cache root whose owning directory the sweep removed is reaped as an orphan, and the pass stays silent unless it acts. Newpruneflags:--no-cache,--cells-root.
Fixed
- Combining stale-lock reclamation with dead-session reaping is refused before mutation so named owner evidence is retained.
- Decode repository-bound brief, PR and issue claim keys before live reconciliation; use the matching board or forge item reader, keeping unknown mappings and unreadable sources blind.
- Dispatch storage admission checks checkout and temporary volumes even when managed Go caches reside elsewhere, and supports an explicit disk floor without managed caches.
- Git reads and pushes from shared-object clones reuse alternate pack indexes instead of decoding them for every borrowed object.
- Keep healthy claims and the standing supervisor running when a claim has an unknown owner, a future timestamp, or a refused stale release; report those claims blind. Already-released claims skip deletion.
- Managed task scratch can retry an interrupted workspace cleanup after restart, preserving ownership and evidence receipts until the disposable workspace is removed.
- Supervisor claim retirement compares the exact assessed claim version, preserving replacement holders, and reads immutable payloads from the active claim namespace. Live ticks use bounded pages with explicit continuation and hold malformed claims for inspection.
- The mutation runners (
muhar,loopadmin/mutate) no longer poison the ambient Go build cache: each mutation run now gets a throwawayGOCACHE(overriding any inherited one) that is removed when the run ends, including on interrupt and error exits.GOMODCACHEstays shared — module downloads are immutable and shared-safe. - The opt-in dead-session worktree reaper recognizes detached review heads preserved by remote refs after squash merges, requiring a named inactive owner and retaining unpublished commits and local review evidence.
cellctlpasses each generated role session and the cell's roster and credential home to worktree initialization. Deskd resolves the repository from the cell checkout and uses the cell's custody context instead of the launching shell's.- graph-execution/07 Verify table re-authored after #1740: row 3 pins
--since/--untilto the fixture range (the default 28-day window made the row clock-dependent), row 7 reads the exit code from a built binary instead ofgo run, row 8 uses--basewith a pinned SHA instead of the--lint-only--diff-base, rows 3 and 5 capture the report to a per-runmktempfile before jq so a pipefail shell cannot report the report's own exit 3 as the row's and jq never reads a file the run did not write, row 4 escapes its Command-cell pipe and the other rows drop theirs so the witness runs each full command, rows 7, 8 and 9 end in a status-carryingtestwith anoutput isExpect so each can fail under the execution witness, and rows 1, 2 and 6 anchor their-runselectors and assert the--- PASS:line.
Changed
- Route all six live board-writer credential consumers through the main-only board-writer Environment. Evidence auto-merge uses a default-branch metadata poll, and changelog rolls require a non-dry-run dispatch on main.
assay release v1.0.38 - statusgen + qualgen + desk-tools + drainloop.
Binaries (pin per-platform by sha256 in .assay-versions):
- statusgen-
- qualgen-
- desk-tools-.tar.gz
Source components (cloned or imported at this tag; pin the umbrella , never a per-component tag):
- github.com/medici-finance/assay/drainloop
desk-tools image (built separately by docker-publish.yml, daily-immutable-tag; pin the ref in .assay-versions):
- ghcr.io/medici-finance/assay/desk-tools:v0.1.0
See checksums.txt for every asset sha256.
Release authorization (traceability to the person authorizing release):
authorized-by: kryton (dispatch actor)
This records who authorized the release cut, not who or what built the artifact; the pipeline carries no signature or provenance attestation, and the human approval in the gated release environment (not this line) is the authorization it names.
v1.0.37
Highlights
Fixed
deskevidencelands Evidence rows and outcome records again. Since v1.0.33 every landing, including--dry-run, exited 5 with "refusing to reach the forge with no minted verifier token": pre-work admission reads the dispatcher's attestation record from the forge, and it ran before the verifier token was minted, so the tool's own custody step refused the read. The landing now mints the token and resolves its forge first, then runs admission through that same forge, so admission and the write use one forge under one minted token. Admission still runs before the landing reads the evidence file or the branch and before any write, no check is skipped or made optional, and there is still no fallback to an ambient identity. One visible change: a landing that admission or a later local check refuses (for example a--rootthat is not the dispatched verifier home, or an oversized file) now mints a token before it is refused. On a GitHub-served repository that mint reuses a cached token inside its reuse window; on a GitLab-served repository the mint rotates the role credential, so such a refused landing now spends one rotation it did not spend before. The refusals placed before admission still refuse without minting: a malformed or unlisted repository, an unsanctionedmain, bad or conflicting flags, a generated or appended-log target, and an outcome record that does not parse, is future-dated or cannot be named. Two refusals that need only the command line sit after admission, as they did before this change, and so now mint first: a target outsidedocs/streams/, and an absolute--evidence-filewith--rootthat is not a--brief-pathfragment outside it.
assay release v1.0.37 - statusgen + qualgen + desk-tools + drainloop.
Binaries (pin per-platform by sha256 in .assay-versions):
- statusgen-
- qualgen-
- desk-tools-.tar.gz
Source components (cloned or imported at this tag; pin the umbrella , never a per-component tag):
- github.com/medici-finance/assay/drainloop
desk-tools image (built separately by docker-publish.yml, daily-immutable-tag; pin the ref in .assay-versions):
- ghcr.io/medici-finance/assay/desk-tools:v0.1.0
See checksums.txt for every asset sha256.
Release authorization (traceability to the person authorizing release):
authorized-by: kryton (dispatch actor)
This records who authorized the release cut, not who or what built the artifact; the pipeline carries no signature or provenance attestation, and the human approval in the gated release environment (not this line) is the authorization it names.
v1.0.36
Highlights
Changed
- The release workflow pins
actions/upload-artifactv6.0.0 andactions/download-artifactv7.0.0 (both run on Node 24), replacing v4.6.2 and v4.3.0, which target the deprecated Node 20 runtime and drew a deprecation warning on thetest (deskmerge)legs of every release run. Inputs are unchanged. The staged and proposed copies of the workflow carry the same pins.
assay release v1.0.36 - statusgen + qualgen + desk-tools + drainloop.
Binaries (pin per-platform by sha256 in .assay-versions):
- statusgen-
- qualgen-
- desk-tools-.tar.gz
Source components (cloned or imported at this tag; pin the umbrella , never a per-component tag):
- github.com/medici-finance/assay/drainloop
desk-tools image (built separately by docker-publish.yml, daily-immutable-tag; pin the ref in .assay-versions):
- ghcr.io/medici-finance/assay/desk-tools:v0.1.0
See checksums.txt for every asset sha256.
Release authorization (traceability to the person authorizing release):
authorized-by: kryton (dispatch actor)
This records who authorized the release cut, not who or what built the artifact; the pipeline carries no signature or provenance attestation, and the human approval in the gated release environment (not this line) is the authorization it names.
v1.0.35
Highlights
Added
- A read that fails while the verifier packet's commit list is built is stated in a fixed sentence of the tool's own; a test fails each of the provider's reads in turn and holds the reader's error text out of the file (#2439).
- A staged copy of the board workflow (
ci/staged-workflows/assay-statusgen.yml) adds an hourlyreconcilejob, pending maintainer promotion into.github/workflows/. It writes the generated Briefs tables in stream READMEs by pull request only: it flips Status cells witnessed by a merged PR'sBrief:trailer, re-renders the tables, and carries any change as one draft pull request onboard/reconcile. It never pushes the default branch, scheduled runs have their own concurrency group so they cannot displace a pending push regen, and the schedule runs only in this repository, never a fork. Each tick recomputes from the default branch, so a change on the default branch never wedges it; it refuses to overwrite a branch carrying a commit whose subject or change is not its own, anywhere on the branch (the check reads a commit's subject and what it changes, renames included, not who made it, and a failed read fails the tick); and it fails when the remote, the pull requests or the PR list cannot be read. Its pull request is the open one from this repository's own branch into the default branch, whoever opened it: a fork's same-named branch, or a pull request from that branch into another base, never stands in for it, and two candidates fail the tick. Each push rewrites the PR title and text from that tick's report, and the report, including any held rows, is printed to the job log.statusgenruns holding only the read token; the board-writer App token is minted only when there is a commit to publish, narrowed to contents and pull-request writes, and only the step that pushes and opens the PR holds it. The job uses the same isolation as the corroborate job (a job-local, checksum-verified Go toolchain and caches, a pinned gh), with Go workspace files off so none left above its temporary directory is read, and reads no git state an earlier job on the runner could leave: it clones the default branch fresh under the job's temporary directory instead of checking out into the workspace, uses git and gh configuration of its own with hooks off, pushes to the forge by URL, and binds its credential helper to that host. Once the file is promoted, the board workflow's pull-request lint runs the job's tests. - A worker packet shows every value it did not write — a title, a login, a branch, a label, a
check or file name, a finding's id and state, an error's text — inside a code span the value
cannot end, and quotes longer text between boundary lines. It reads a finding record only
from a review the forge attributes to the reviewer identity: a review by any other account
is listed under its own heading as not the reviewer's, and when that identity cannot be
resolved no review is listed as the reviewer's, no record is read and no body is quoted. - Both worker kits gain three clauses about how a run gathers and waits — batch independent
reads into one request, read the packet first when the assignment names one, and wait on a
check or a review in one bounded command — none of which changes what a worker must do or
wait for. - Every
deskreadenvelope carries an additiveidentityobject (app-custodywith the role, orci-workflow-tokenwith the repository and run id), and one stderr line names the transport. The token never appears in output. - In a worker packet for an open change, a finding record in a review at an earlier commit is
judged against that review's commit:resolvedwith no evidence commit of its own is not a
resolution at the current head, and the line says so. Comments are quoted newest first, so
the packet's cap leaves out the oldest; each comment, and each review by another account,
says whether its author is the reviewer identity, on the trusted list, not on it, or not
checked because no list is configured; a comment by an account that is neither is quoted
only up to 16 KiB (#2439). - Review kit clause 20 and the
pr-review-deskskill make the lane round cap bind: from a lane's fourth round, a finding first raised then, in code unchanged since the lane's first review, is advisory or an arbiter hand-off, except a safety-relevant finding, which still blocks. A late finding the reviewer cannot place with confidence blocks, the cap never changes a security verdict, and a finding whose evidence did not exist at the first review (the base branch moved, the description was edited, a check result at the head, a could-not-check gap, an undeclared decision) is not a late finding. - Text taken from the change is marked as data in the packet two ways. Multi-line text sits between two boundary lines that carry a random value made for that one file; inside them, a line that would begin with three less-than signs is prefixed with
[quoted], and line-break characters other than the newline are written as\uXXXX, so no quoted line begins with a boundary mark. Single-line values (title, branch, check and file names, logins, error texts) are written in code spans in which a backtick is shown as an apostrophe. The packet's opening lines tell the reader both. - The
// regression: #<N>doc-comment tag (alsoF-<slug>,class #<N>) on the line above a test says which incident it pins; seven existing regression tests carry it. Only a line holding refs of those shapes and nothing else is a tag: doc-comment prose that happens to wrap onto aregression:line is not. - The outbound-write check can consult a deployment's own executable, the house callout. Set
ASSAY_OUTBOUND_CALLOUTin the roster to an absolute path; every outward write the compiled checks have passed is put to it AFTER them (a push: once for the branch name, once per commit message, once for the added lines), as one JSON object on stdin with the text as plain JSON strings (no HTML escaping), and it answersalloworblock <reason>, the first word exact and lower case. It can only narrow: a compiled refusal is never put to the callout, and a calloutallowclears nothing. Any failure of the callout (missing, writable by group or world, non-zero exit, timeout, empty or oversized output, an answer that is neither word) refuses the write, and the message names the failure. Every refusal is thehouse.calloutrule and is not overridable by--force-scan-override. Everything the callout printed — a block's reason, an answer outside the vocabulary, its own stderr — goes to stderr only, on one line, never to the audit log, the error text or the forge. The callout runs with an environment of exactlyPATH,HOME,TMPDIRandLANG, holding the calling process's own values; a callout sets its ownPATHand its own locale, as the example does (PATH=/usr/bin:/bin,LC_ALL=C), so the caller'sPATHorLANGcannot decide its answer. When the callout answersallow, anything it wrote to stderr is still printed to stderr. - The review kit gains two clauses on every lane. Clause 17 tells a reviewer to read a file in the fewest requests (normally one read, whole; one targeted read of a large file the PR barely touches), to send independent reads in one request, and to gather a PR's state in one command, because every request re-reads the whole conversation. Clause 18 tells a reviewer to read a prepared review packet first when the assignment carries a
Packet:line; it is inert when no such line is present, only the dispatcher's assignment block can arm it, and the PR's head and every check are still read from the forge before the verdict (#2437). - The verifier kit gains clause 7, "Packet first — a reading aid, never evidence": read the packet first, whole, in one read, fetch only what it lacks, and never copy a packet line into an observed cell or count a row as run because its command is quoted. A row's result comes only from running the row at the verified commit (#2439).
- The verifier packet finds a brief's Evidence and Verify headings the way the repository's own readers do, with the white space around the line taken off first, so an indented heading no longer leaves an Evidence section in the packet. It no longer says it holds no result: it says what it cut, and where it finds no Evidence heading it says it could not determine whether the carried text records an earlier result. The result-like-column check now covers every table in the carried text, and a numbered or emphasised Evidence title cuts too (#2439).
- The verifier packet writes every value it read from the repository and shows at its own level — the brief's path, a commit id, the brief id — with
packet.Code, so a path chosen by a brief's author cannot close its code span or start a line of its own; copied text stays between a boundary pair, where a line that would begin like a boundary line is shown with[quoted]in front (#2439). - The verifier packet's commit list says how many commits it left out when it stops at its limit of six, and says when no commit message was searched because the item key gives no brief id. The verifier kit's packet clause says what to do when one read does not return the whole packet (#2439).
- The worker-desk skill adds a rule for new dependencies, plugins and MCP servers: before install, file an issue with an agent-generated risk summary covering source, permissions and persistence. The minimum release age is 7 days, and a younger release also needs a driver
bless. The worker dispatch prompt carries the same rule. - Versioned source obligations and project applicability:
spec/project-obligations-v1.md,schemas/project-obligations-v1.jsonand an offline loader library instatusgen(tested, not yet called by any lint check, command or workflow) that bind a permitted source revision to existing requirement ids and accept an applicability decision only as a link to...
v1.0.34
Highlights
Added
- Design-decision record
DR-forge-neutral-34(docs/streams/decisions/): a transcription of the
driver's ruling on decision issue #2315, which answered option 1 — approve as briefed the
deskreadCI workflow-token transport, an explicit, CI-only, read-only opt-in beside the App
custody default. Brief forge-neutral/34 now cites it throughdesign:, which is what the
design-approval gate reads. The record does not decide the implementing pull request (#2377)
or the brief's later sign-offs. deskevidence --outcome-recordnow writes the verify-wake receipt on everyverify-failorblockedrecord. The receipt's inputs are onefile:hash per path in the brief'sfiles:list that exists at the record's sha (every file under a listed directory), the brief as it lands, and the tool version.blocker_kindandblocker_refare required. Ablocker_refthat is not an issue or change reference, such asto file, is refused with exit 5. Averifiedrecord carries no receipt.deskevidence --outcome-recordstamps the landing repository as the record'srepowhen the caller gave none. A record that already carries a verify-wake-v1 receipt lands only if that receipt is complete; otherwise it is refused with exit 5, naming the missing field.docs/verify-wake.mddocuments the hold rule.verifyloop plan --no-forgereads no blocker issue, so every held brief's blocker is could-not-check.verifyloop planholds a non-pass brief until something it depends on moves, following the hold table indocs/verify-wake.md. It hashes the declared inputs again, and it reads the blocker issue through the forge as the verifier App. A brief whose inputs are unchanged and whose blocker is open prints aWAITline naming the blocker, the wake condition and the next actor. A new summary line readsverify-desk plan: wait=<n> dispatchable=<n> could-not-check=<n>. A missing or rejected token, or any other failed read, is could-not-check, never a closed blocker. A newer explicit-recheck receipt still overrides a hold.verifyloop planreads a blocker only in a repository inside the configured repository set. A reference outside the set is could-not-check, and no credential is requested for it. Each repository is named on stderr before its first read. A bare#Nin a record with noreporesolves in the plan root'soriginrepository.
Changed
- The
STATUS.mdAwaiting board now sorts each brief into one of four owned queues (Awaiting human gate, Awaiting implementer rework, Environment-blocked, Runner-pending) or into Desk-actionable, which keeps only rows that need judgement. Every row names its owner and next act, and the headline shows a count for each owner. A row whose Evidence or verify-outcome records cannot be read renders as could-not-check instead of being placed in a bucket. The rules are indocs/board.md. TheEnv-blockedheading is renamedEnvironment-blocked. A recorded blocker (verify-fail,fail,blocked,needs-context) routes by its kind:implementationandcheck-definitionto workers,human-actionto the driver,environmentto the operator. A recorded hold stays a blocker whatever the Evidence says. A recorded fail is dropped only when the Evidence's live PASS was written on a later day than the record; when the two cannot be ordered, the row is could-not-check. A human-gated brief with a recorded blocker stays with the driver, and its next act names the blocker instead of the sign-off. A runner-pending row needs at least one row left to run, and an unrecognised or future-dated outcome record is could-not-check. Could-not-check rows count toward the verification-debt measure, so an unreadable input cannot switch the alarm off. - The pr-review-desk skill's generated-table bounce gains carve-out C. A one-row hand flip to
implementedis admitted for a brief delivered into another repo when the delivering PR could not carry the brief's trailer. The flip PR carries that row and nothing else; it names one merged delivering PR, which must have merged after the brief was authored and added at least one file the brief'sfiles:list names; and every concrete file in that list exists on the delivery repo's main. Carve-outs A and B are unchanged.
assay release v1.0.34 - statusgen + qualgen + desk-tools + drainloop.
Binaries (pin per-platform by sha256 in .assay-versions):
- statusgen-
- qualgen-
- desk-tools-.tar.gz
Source components (cloned or imported at this tag; pin the umbrella , never a per-component tag):
- github.com/medici-finance/assay/drainloop
desk-tools image (built separately by docker-publish.yml, daily-immutable-tag; pin the ref in .assay-versions):
- ghcr.io/medici-finance/assay/desk-tools:v0.1.0
See checksums.txt for every asset sha256.
Release authorization (traceability to the person authorizing release):
authorized-by: kryton (dispatch actor)
This records who authorized the release cut, not who or what built the artifact; the pipeline carries no signature or provenance attestation, and the human approval in the gated release environment (not this line) is the authorization it names.
v1.0.33
Highlights
Added
- Offline acceptance runner
tests/stream-brief/32.sh(legacy, identity, negative, flow, dereference, mutation) with fixtures understatusgen/testdata/streamview. - Optional authored
mission:block in stream README frontmatter (outcome, success criteria with evidence references, commitments, exclusions). Legacy streams need no migration; an invalid block is a statusgen lint problem with explicit diagnostics and is never replaced by README prose. - Stream view contract v1 (
statusgen/streamview,docs/stream-view-contract.md): an importable, versioned wire type for one stream's state — repository-qualified identity (owner/name+ slug, cell context kept outside the key, renames start a new identity), per-section provenance with explicit availability (available,partial,could-not-check,not-assessed; missing never reads as a value), a fixed trailing 24-hour changes window, version negotiation, a strictDecodeconsumer, andCheckBindingsto dereference recorded revisions and evidence in the repositories they name. Every recorded revision must be a full commit id, and the window must end at the view'sgenerated_at. - A findings entry can now carry an effectiveness record (
effectiveness:,effectiveness-date:,effectiveness-by:, required together or not at all).statusgen --lintfails aresolved: yesfinding dated on or after 2026-10-08 that has none, and advises on earlier ones. The check covers presence and attribution only, not whether the named command would catch a recurrence. - A source or bundle dated after the caller's "now" answers could-not-check, never fresh.
- Design-decision record
DR-cellctl-cobra(docs/streams/decisions/): a transcription of the
driver's ruling on decision issue #2240, which answered option 1 — accept the preserved
source/admission boundaries and the listed help and parse changes of thecellctlCobra and
Viper migration, subject to independent review and all required validation passing. Brief
desktools-v2/16 now cites it throughdesign:, which is what the design-approval gate reads.
The ruling came after the implementing pull request (#2391) was opened, and the record says
so; it does not decide that pull request's review or its merge. - Design-decision record
DR-outbound-callout(docs/streams/decisions/): a transcription of
the driver's ruling on #2375, which answered option 1 ("As proposed") for the deployment-supplied
executable the outbound-write check may consult. Brief desktools-v2/11 now cites it through
design:, which is what the design-approval gate reads. The record states that the
implementing pull request #2383 was built before the ruling, and it does not decide that pull
request's review or merge. - Existing quality evidence informs a scoped remediation decision; high complexity or touching a file does not automatically authorize a refactor.
- Existing results carry the fields statusgen's remaining forge sites read:
PullRequest.MergeCommitSHA(merged changes only, never an open change's test-merge SHA),Issue.ClosedBy(closed issues only, so a reopened issue no longer names its former closer),ChangedFile.PatchwithPatchAbsentstated when the forge withheld a diff,Comment.UpdatedAt(empty when unread, never the creation time), andChangeRef.Author,BaseRef,CrossRepoandHeadRepo(an unreadable fork stays empty, never same-repository or the base). AuthorTypestays empty wherever the forge did not resolve it. - House-cell startup opens one configured comms service surface in tmux, Herdr or Orca; shutdown stops its supervised processes and preserves mailbox data.
- Its queries tell present, known-negative, stale and could-not-check apart.
- New pure Go module
deskcore(no third-party dependencies) with four effect-free packages: - Opt-in cell-owned Go caches persist across task homes, with byte budgets, filesystem free-space floors, active-consumer protection, JSON cleanup reports and retryable storage deferral before dispatch claims work.
- The App inventory in
docs/adopting-assay.mdnow records the workflow App: the single identity holdingworkflows: write, scoped to exactlycontents: write+workflows: write+pull_requests: write+metadata: readwith zero webhook events, and withadministration,actions,checks/statuses: write,membersand secrets/variables withheld. It stands beside the desk-role Apps as a capability, not a desk role or a tier. - The forge seam grows four typed reads under the freeze rule (forge-neutral brief 33), each with a
deskreadkind as its consuming call site and both backends pinned on recorded fixtures:ListIssues(any state, one optional label, the issue population only, withIncompleteandPageCapwhen it overflows the page ceiling),IssueStateEvents(close and reopen events with their actors, plus the changes that closed the issue, withComplete=falseon overflow or on any closer whose merged state cannot be read),ListChangeCommits(a change's commit SHAs,Complete=falsewhen the change's own commit count is larger than the list) andRepoDefaultBranch(an empty answer is could-not-check, nevermain). - The fork-test block is read by a strict, fail-closed grammar (
tools/desk/README.md):
fenced code blocks and HTML comments are blanked first, following CommonMark for those two
constructs (a fence closes only on the same character, at least as many repetitions and
under 4 columns of indent; an unclosed comment hides everything from its line to the end,
wherever on the line it opens; a blanked line stays as an empty line, so it never joins what
came before it to what came after it). The heading must be followed directly by the block,
and the block is the contiguous run of column-zero<lowercase-key>: <content>lines. Prose
between the heading and the first key line is refused as a malformed block, naming that
problem. The strip models only fences and comments; a backstop drops the declared subject
when the raw body has more than one fork-test heading, when a reading of the raw body
disagrees with the stripped reading, or when the raw body opens a raw-HTML block kind a
renderer drops whole (processing instruction, declaration, CDATA). One known limit is
stated: asubject:line placed directly under the block's last line, with no blank line,
joins the block. - The host contract for a split run, stated in
verifyloopusage: the host writes its own dispatch record (repo, head sha, dispatch time) before starting the fence and runs the composer with--repo/--shafrom it; signs only after the composer exited 0 with exactly onesha256=value and the fence has fully exited; passes all five binding flags, taking repo, head and both time bounds from its own dispatch record and only the digest from the composer; takes the signed body from stdout, never from a.outthe fence could write; and signs only a payload moved by rename into a directory the composing uid cannot write. The binding is checked only at signing: the host refuses a payload whose digest, repo, head or ts does not match its own dispatch record. It does not limit what the payload's entries claim, and nothing after signing checks head or ts again (statusgen's transcriber checks only the repo), so a hostile row can still invent results that the host then signs. - The notice lane reads its reversible signal from the block's own
subject:line alone,
never the title or body prose, and only when the bounded block carries exactly one such
line; none or several admit nothing. A shape-only needle (lint level,lint severity,
notice or error,port-or-drop) never admits and vetoes any content needle beside it,
in its plural spellings too (-s,-es, and-iesfor a needle ending iny).
Needles match on word boundaries, with runs of spaces, underscores, dots, slashes and
hyphens treated as one space. The CI check/job backstop reads the subject the same way, and
any non-ASCII character in the subject fails closed. - The reader refuses unknown schemas, inputs over 16 MiB, and any key the schema would refuse.
- Two workable options plus a gate the driver still holds can file on a NOTICE LANE
(desk-decided, off the driver's queue, with the shareddesk-r3-decision v1marker)
rather thanneeds-decision. Admission fails closed: the item must carry a positive,
content-bearing R-3 reversible signal (docs wording, typo, phrasing, a table column — never
a shape-onlytool default/default value/flag default/rename the, and never a
lint level/lint severity/notice or error/port-or-dropexample either, which is
always a classification question about some check or job, named or not) AND no one-way
signal — a one-way caller
label (human-only,security,gate:human), adeskkit.HumanOnlySignalsneedle, or a
deskkit.OneWayPatternsmatch (merge, ready-flip, main push, tag/release, weakening a
security control, secrets/keys/PII, money, identity/auth, deleting or overwriting data,
sending or publishing outside, live infrastructure, App permissions, approval authority,
gate:human, direct writes to main, draft-to-ready, required reviews, 2FA/MFA, hooks and
signatures, the trust gate and roster, owners and roles, archive/transfer/visibility,
auto-closing driver-owned items, charges, and turning a control off). A one-way term
outranks a reversible one, and theruled-check:line is read for one-way terms (exempt
only from therulingneedle). The same one-way check refuses--no-forkand withholds
the re-routes from the fewer-than-two-options refusal. The patterns are a floor, not a
guarantee: review of the filing and the digest's veto window are the layers around them.
deskdigestlists notice-lane items in its desk-decisions section with their veto date,
and drops adesk-decideditem from the...
v1.0.32
Highlights
Added
- A class guard over the desk tree refuses any new function that reads a forge token (
GH_TOKEN,GITHUB_TOKENand the like) from the process environment unless a reviewed permit names it. - A class guard refuses any shipped function that looks a comment up by id on the untyped first-page comment read; such lookups must use the typed read, which walks the whole thread.
- A pull request read now reports whether its head lives in the base repository or in a fork, on both the GitHub and GitLab backends.
- CI now runs the findings-register transition check:
statusgen --corroboratein its owncorroboratejob ofassay-statusgen.yml(statusgen/06 deliverable 7, #1855). No PR-authored code runs in that job. The guard binary is built from the merge commit's base parent, with a job-local, checksum-pinned Go toolchain and caches (GOENV=off,GOTOOLCHAIN=local) and the pinnedgh, and the PR tree is read only as data. Before building, the job checks that this parent is the merge-base of the base branch with the merge commit, so the guard compares against the commit it was built from, and that the event's base sha is its ancestor. A base retarget or a PR edit re-runs the job, no later event cancels a pending run of it, and a retarget during a run fails it. It runs on PRs that touchdocs/streams/findings/, and fails closed when the base cannot be read or tied, or no human-login map is configured. Residual: underpull_requestthe workflow file itself comes from the PR, so a PR that editsassay-statusgen.ymlcan change this job; that edit is caught by human review of workflow changes, not by the job. - CLI routing registry (
docs/streams/desktools-v2/cli-migration.json) and its check (tools/desk/internal/clicontract): every Go command and script launcher in the tree is discovered independently and routed to an owning migration brief or a classified exclusion; TestCLIInventory fails on an unrouted, stale, orphaned or over-budget row, and TestCLIOwnerMigrated runs each migrated package's contract tests. - Check descendant files for directory readers on both workflow events, and retain rejecting controls. Give preserved shell fixtures bounded runtime headroom with a cancellation control.
- Design record
DR-desktools-v2-03for the read-path custody ruling. - Design-decision record
DR-cellctl-go-port(docs/streams/decisions/): the operator's
ruling thatcellctlis ported from bash to Go, with the bash kept as the oracle until the
Go port reaches parity. Brief desk-containers/10 now cites it throughdesign:, which is what
the design-approval gate reads before that brief can move toin-progress. The record
describes the tree as it stands (the Gocellctlalready ships in the release tarball since
#1377) and does not decide the brief's cutover sign-off, which stays its own human gate. - Desk CLI adapter (
tools/desk/internal/cli): a fresh Cobra tree and command-local Viper per invocation, help and version answered before any config, credential or effect, allowlisted configuration sources with unset kept apart from empty, no secrets on argv, and parse errors exiting 2 (desktools-v2/15). - Findings-register parks are capped: a
parked-untilthat is added or extended may run at most 90 days past the date of the commit that sets it (a value not yet committed counts as set today, and a commit dated in the future counts as today). A later date is a--lintPROBLEM and MISSING in the--corroborateregister lane, whoever authorized it. A landed park left as it is, or narrowed, is not re-judged. - Register 26 inherited desk-tool and statusgen behaviors with parent-counterfactual evidence, a manifest guard, and fixture-only regression entry points.
- Route a durable task controller, runner conformance/adapter, immutable candidates, bounded specialist review and exact-candidate publication into nine implementation briefs.
- Rule-register rows
R-ambient-token-readandR-signoff-read-whole-threadindocs/contracts.md. - Run every floor fixture that starts git, directly or through a shell, without the caller's GIT_* variables, so a GIT_DIR exported by a git hook cannot redirect fixture writes; a hostile-GIT_DIR control and a structural guard pin it.
- Stage the additive statusgen full-test CI case; its enforcement remains held until the maintainer applies the workflow patch.
- Start every floor runner's go tool through one wrapper that clears the caller's GIT_* variables and global git config, so reused manifest rows cannot write to a repository an exported GIT_DIR names; a runner-level hostile-GIT_DIR control, a choke-point guard and a mutation mode pin it.
- The shared CLI contract (
docs/streams/desktools-v2/cli-contract.md) and migration briefs desktools-v2/18–54, one complex command or up to five simple ones each, with desktools-v2/17 now depending on all of them. - The shared loop-admin runner protocol (
spec/loop-admin-runner-v1.md,schemas/loop-admin-runner-v1.json) and the independentloopadminGo module that implements it (loopadmin/runner): start, observe, cancel and reconcile under a stable launch identity, strict standing-desk and workflow-stage modes, capability declarations with mandatory-field refusal, a generation fence on result acceptance, credential exclusion that decodes and walks extension values (nested keys, escaped tokens, Basic credentials, auth, bearer, private-key and access-key slots in any spelling or prefix, a secret written after a setting word, URLs carrying a password), with a short list of described exceptions (auth: disabled,auth_method,pallbearer, a port with an@in the query), strict decoding (exact-case keys, no repeated key, no null for a defined field or a list element (refused at decode), no blank tool name (the schema refuses empty, validation refuses whitespace too), nothing after the request), terminal states that absorb any later report, a result that must echo its invocation identity and a reported model, refusals that never echo request or result content, adapter and fence errors sealed to the contract's sentinel (still matchable witherrors.Is) on every verb, and records the caller and the adapter cannot rewrite after the call. Unknown outcomes, missing usage and unconfirmed cancels stay unknown. It ships an in-memory fake adapter, an offline conformance kit, a fixture that bypasses the contract's fence to show the caller's own claim-generation check still refuses a fenced result, and a mutation gate. No provider client or provider call. The module's CI job is staged atci/staged-workflows/loopadmin.ymlpending maintainer promotion. assay:cut-release— a release-cutting skill in the public bundle, written against the singlevX.Y.Zumbrella release. It sets the order of the chain: four live preconditions, then both release-workflow runs handed to the driver as exact commands — the dry run first, with its dry-run input set explicitly because the workflow defaults it to off, then the publish — since both wait at the release environment's human approval and record their dispatcher as the release's authorizer. Publication inputs such as the image push stay off unless the driver decides otherwise. Hashes are taken only from the publishedchecksums.txt, then every consumer is re-pinned. The chain is done only when every consumer is green on the new pin. A bad release is fixed forward; a tag is never moved. Adopters cutting their own releases use the same skill. It is registered in the skills README, SOURCES.yaml, the Codex and Cursor packaging rosters, the per-harness capability tables, and the Codex smoke protocol's skill list.statusgen --corroborate --pr Ngains a findings-register transition lane: for a PR that touchesdocs/streams/findings/, every caution-removing move of a finding'sresolved/affects/ack/parked-untilsince the PR merge-base needs a human named in the entry's authorizing key to have approved or approved-by-comment on that PR. An anchor already on the entry no longer authorizes a later change by itself. Whether a PR touches the register is read from the local tree against the merge-base, so a forge file listing that stops short (it caps at 3000 files) cannot hide a touched entry. An unresolvable merge-base with a listing that cannot be shown complete, or an unparseable touched entry, fails closed.
Fixed
- A plain
statusgen --lint(no--forge) no longer reaches the forge: before this change it ran the claim read (git ls-remote --heads origin) and dead-claim decay (gh pr list, or the GitLab API). Offline, the claim set now reads could-not-check, and--require-claimsfails closed on it. The offline-lint test now records every forge/network process and HTTP request, so a read that starts and then fails is caught. - A role transport failure after the URL writes restores the worktree's prior push destination, and no inherited credential helper answers inside the retained worktree.
- Desk-tools tests that depend on a newer git now skip with a named reason on an older one instead of failing for the wrong reason. On a Linux verify witness whose image ships git 2.39, the
deskevidencemerge-tree proxy test reports "needs git >= 2.43 for merge-tree --attr-source; have 2.39.5", and thedeskwt add --role/role-inittransport tests (plus onedeskkitpush-transport case) report that they need git 2.46 for the empty-entry URL list reset. On git that meets the floor every one of these tests still runs in full. A new stdlib-onlyinternal/gitversionpackage holds the sharedRequireGitguard. - Deskboard no longer shows MERGE-NOW for a ready, risk-classed PR that has a correctness approval at head but no
Security-Review: passat head. The security hold used to apply only to drafts, so a ready PR that took a new head and a correctness re-approval, with no fresh security pass, read MERGE-NOW. Such a row now reads SECURITY-REVIEW-REQUIRED whether i...
v1.0.31
Highlights
Added
- Advice can only restrict.
- Agentic admission draft (
spec/agentic-admission-v1.md,schemas/agentic-assessment-v1.json,deskkitEvaluateAgenticAdmission). It is a pure, deterministic policy that maps hard facts and recorded advice to one of five dispositions: bounded-agent-work, supervised-agent, human-led, discovery-only or blocked. - Malformed input fails closed: empty bindings, invalid or oversized subjects and inputs, and out-of-vocabulary values. Reason codes never echo an assessed string, and a refused subject is never echoed into the result or its
PolicyResultprojection. - Mandatory graph gates are the union of the brief's risk verdict and the disposition's.
- Nothing activates it yet; dispatch wiring is a separate gated change.
- Unknown readiness holds implementation.
- discovery-only stands only on a discovery grant whose read scope covers the subject; otherwise the result is blocked.
Fixed
- Forge-ref claim writes fail closed without a complete matching receive-pack acknowledgment; HTTP refusals retain their original status marker. Optional trace receipts retain bounded request and pack metadata without claim payloads or credentials.
- The build-less-brittle/04 and /06 Verify tables now reach a verdict after the briefs land. Each
consumers:row runs in a throwaway clone pinned to the delivering squash, and Expect names exit 0 and the exact summary line. Row 9 of /06 prints one gated count. Row 7 braces its revision variables, so zsh can no longer pass it at 0 ≤ 0. Row 3 keeps its scratch files in the checkout. What each row proves is unchanged. - The outbound check no longer refuses a brief's own frontmatter
id:as a session id (#2022). The exemption covers exactly one shape: a lowercase UUID that is the whole value of the singleid:line inside the frontmatter of adocs/streams/**/brief-*.mdfile. The check reads the file's full content to establish that shape, on the push path, the forge file write and the evidence pre-flight. A UUID in brief prose, in any other file, in a commit message or in a PR body is still refused. - The publish-identity gate in
deskpr updatenow judges only the commits the push adds, so a PR
whose head already carries a commit by another trusted App can be updated. The narrowing fails
closed: a missing, stale, diverged or oddly-spelled remote tip falls back to the whole
origin/<base>..HEADrange, andupdatere-checks against the forge's live PR head before it
pushes. deskpostno longer carries the retired per-item+1reaction gate's read path: the orphaned reaction-read method on its forge-backed adapter is gone, the adapter's interface comment now names only the live-visibility read the:publicwrite gate consumes, and a structural guard test fails if any reaction/award read reappears anywhere indeskpost.
Changed
- Define Go launch and session contracts for the native Windows cellctl work, with strict record parsing, independently bound launch permissions and compiled process fixtures. Production Windows integration remains separate work.
deskboard actionsno longer skips review of a PR authored by a trusted or blessed human: theHUMAN-OWNEDaction is retired, so such a PR isNEEDS-REVIEW(orRE-REVIEWon an advanced head) like any other admitted PR and counts toward the UNREVIEWED neglect alarm. The trust gate (trusted roster login, else a current blessing) is now the only authorship filter: an unblessed author stays quarantined in EXTERNAL / UNBLESSED, and an unreadable trust read or roster still keeps the PR out of dispatch.reviewloopdrops the matching table key, so an older board that still emitsHUMAN-OWNEDreads as an unknown action and fails closed (#2028).- ci: promote the staged Windows CI leg and the winparity PowerShell 5.1 parse check into live workflows.
assay release v1.0.31 - statusgen + qualgen + desk-tools + drainloop.
Binaries (pin per-platform by sha256 in .assay-versions):
- statusgen-
- qualgen-
- desk-tools-.tar.gz
Source components (cloned or imported at this tag; pin the umbrella , never a per-component tag):
- github.com/medici-finance/assay/drainloop
desk-tools image (built separately by docker-publish.yml, daily-immutable-tag; pin the ref in .assay-versions):
- ghcr.io/medici-finance/assay/desk-tools:v0.1.0
See checksums.txt for every asset sha256.
Release authorization (traceability to the person authorizing release):
authorized-by: kryton (dispatch actor)
This records who authorized the release cut, not who or what built the artifact; the pipeline carries no signature or provenance attestation, and the human approval in the gated release environment (not this line) is the authorization it names.
v1.0.30
Highlights
Added
- A new
forgebanrule stops any package outsideinternal/deskkitfrom naming a backend type, so no code can build an unchecked backend or unwrap the checked one. - Document the native Windows implementation plan for
cellctl, with dependency-ordered briefs covering Orca and Herdr launchers, Docker lifecycle, credential custody, and Windows acceptance checks. Runtime support remains planned. - Draft continuing-operations specification and parked stream proposal, with fourteen delivery outlines covering user journeys, portable measures, expected impact, outcome review, adopter packaging and cockpit integration. These are review materials, not released runtime capabilities.
- Every new brief now carries a
design-fit:block in its Context (owner,contract,retires,weight,why-add), so the author answers which module owns the change, what it retires and how much weight it adds before a worker starts. Defined inspec/brief-v1.md§4.1, with an example indocs/brief-template.mdand the author-brief skill template. - Every write verb takes the same audited
--force-scan-override. Credential, personal-data and self-containment refusals can be overridden, and each override leaves an audit row that holds the rule id and a digest of the text, never the text itself. A withheld identifier on a public or unknown target, and a ruling claim, can never be overridden. - Hotspot report for
tools/desk: a test-only package (internal/hotspot) that ranks files by churn × indentation complexity over first-parent git history and reports temporal-coupling pairs (go test ./internal/hotspot/ -run TestPrintHotspots -v -args -since=… -until=… -top=N). A shallow clone reports could-not-check, never an empty ranking. No verb or flag is added to any shipped binary. - Known limit of the critical tier's inputs: a brief's
issues:list (the main-red fix linkage), a README stamp cell, and a findings entry'scontrol:are repo text that an ordinary reviewed PR can write. Only the red itself (--main-health) and the stamp authority set (roster configuration) come from outside the tree, so tier membership is as trustworthy as the review and merge of those files. - Negative-path tests pin the custody contract of the desk's native GitHub read client: a read with no minted token is refused before any request, even with a
GH_TOKEN,GH_ENTERPRISE_TOKENor gh login present, and the installation a read's token is minted for is the account of the repo being read, never oneGH_REPOorGH_TOKENnames. A committed mutation spec proves each check is load-bearing. - New
ReviewQueueSnapshotForge operation. It returns every open change together with its reviews in one backend round-trip: a single GraphQL query on GitHub. On GitLab it is degraded: it marks each change incomplete, so reviews are still read per item. - One outbound-write check now runs on every Forge write the desk tools make, and on every push made through
deskpror from a checkout with thedeskpushguardpre-push hook installed, before the write leaves the machine. It combines the credential scan, the ruling-claim guard, a new personal-data pass (e-mail addresses and international phone numbers) and, for public or unlisted targets, the self-containment categories and the withheld register. The target's configured visibility decides which layers run. - Platform path, custody ordering, private fixture directory, hook argument forwarding shared forge contracts, backend read boundaries, GitLab outbound note and rewrite cases, and two-forge ambient credential checks. The compatibility brief remains incomplete pending owner scope clarification for environment resolution and Windows outbound-machine-path recognition.
- Review gains a design-fit stage that runs before the correctness pass on any PR that grows a ratcheted weight dimension, adds an
R-rule row, touches a brittle-marked module, or reddens aninternal/archtest. Each trigger applies only where the repository carries the instrument it reads; a missing instrument is recorded as could-not-check, never as a design-fit finding. It asks three questions: is the change in the right layer, could removal fix the symptom instead, and areretires:/why-add:true. A "no" answer is a finding with the new scope basisdesign-fit(deskkit.BasisDesignFit, pinned to the review kit'sreviewscopeblock). The class starts advisory: the finding is recorded and does not hold the PR. Promoting it toblockinglater is a one-cell edit in the pr-review-desk finding-class register. - The
observeevidence kind (spec/workflow-pattern-v1.md,schemas/workflow-pattern-v1.json):
a signal watched over a window after a change lands, declared only where a deploy
exists. - The check sits in two places. The Forge that
ResolveForgereturns wraps every write that carries text, so a verb cannot hold an unchecked Forge. The push path (deskprbefore it pushes, and thedeskpushguardpre-push hook) checks the branch name, every commit message and the added lines of the diff. A file written through the Forge checks the branch it lands on the same way. A comment, review or edit on a numbered item carries that number into the check, so a bare#Nabove it is still named as a probable cross-repo reference. - The worker-desk skill states the drive worker floor: for drive work, the floored
deskboard dispatchreading wins over the widerfanoutloop planboard queue. - Verify rows can name their command explicitly: in a Command cell that mixes prose and code spans, a code span starting
cmd:(for example`cmd: go test ./pkg/ -count=1`) is the command.statusgen verifyrun, the check:ci re-execution lane,newbrief, the row lint,verifyloopanddeskrebaselineall take the firstcmd:span. A cell whose first span is already its command needs no marker. A cell without the marker lifts exactly what each tool lifted before, so no existing row changes command. A marker counts only where the rendered brief shows it as code: one between escaped backticks, or in a cell whose prose carries raw HTML, a comment, a link, an image, a dollar in any spelling ($,\$, or a character reference such as$, which GitHub can render as math) or any character reference, or in a cell where a span's opening backticks are fused to the text before them (an autolink or~~can swallow them), is ignored, and the marker span itself must be set apart by whitespace or the cell edge (plain punctuation may follow it), so hidden text cannot replace the command a reader sees. - Work-input dependencies (graph-execution/03 work-input amendment): a manifest's
policy, build and environment dependencies are fingerprinted at the witness's commit
and the item's revision. A changed one holds only the claims that depend on it, as
wrong-revisionnaming it; one that cannot be fingerprinted iscould-not-check. A
reused ancestorpassnames its derivation and keeps the witness's own revision, so
an old receipt is never retargeted to a new subject. TestOracleShellcheckCleanintools/desk/cmd/cellctlrunsshellcheckover the whole oracle insidego test, so wherevershellcheckis installed a new finding at any site is a red test. CI does not installshellcheckyet, so there the test skips, and a non-verbosego testprints that skip as a plainok; the install step is tracked in #1875. A planted-SC2086 positive control makes a broken or stubbedshellcheckfail the test rather than pass it; with noshellcheckon PATH the test skips as could-not-check.cellctlmanages local Docker cells directly in Go throughCELL_CONTAINER_CONFIG, including registration, preflight, status and shutdown. Native cells no longer need an external host launcher.deskapps init --manifest <file>— registers a single arbitrary GitHub App (e.g. a
leak-sweep gate App) from a manifest JSON file instead of a tier's fixed App set, through
the same loopback bind, state-nonce, callback/conversion/PEM-write and identity-mismatch
machinery as--tier, keyed by the App's manifest name rather than a desk role. Mutually
exclusive with--tier; refuses a manifest that specifies its ownredirect_urlor
hook_attributes.url.deskapps init— the loopback web app that drives GitHub's App Manifest flow: it posts
the chosen tier's manifest (team: read+act;family: one App per desk role) to GitHub's
own new-App page, exchanges the returned code for the App's credentials, and writes the
private key (0600, never printed or logged),apps.envand the role→App bindings. Binds
127.0.0.1only, refuses a/callbackwhose state does not match a pending row, and refuses
a conversion whose App owner is not the account the operator named (ghlogin or--org), failing closed when either owner is empty.
The posted manifest carries nohook_attributeskey (this flow sets no webhook URL, and
GitHub's schema rejects a url-lesshook_attributes).deskboard dispatchapplies the same floor across every configured root, summing in-flight drive work, and names the floor in its held-back line. Its merged queue keeps the critical tier ahead of score, so a critical row takes the shared headroom before routine drive rows, and each row it offers is taggeddrive:<slug>/critical:<arm>. The cross-root floor needs statusgen at this version on every root: a root on an older statusgen reports no cap, and with no root reporting one no cross-root floor applies.deskboard's actions sweep now reads each repo's open PRs and their reviews with this operation, instead of one review read per PR. On GitHub that means 1 call per repo instead of 1 + N. Each PR's head and its reviews now come from one consistent snapshot, and the board output is unchanged.docs/contracts.mdgains a Rule register: one row per enforced rule (owner, invariant, the semantic-index row it serves, justifying publ...