Skip to content

chore: sync fork with bytefolk/mem main - #2

Closed
sun-970 wants to merge 31 commits into
sun-970:mainfrom
bytefolk:main
Closed

sun-970 wants to merge 31 commits into
sun-970:mainfrom
bytefolk:main

Conversation

@sun-970

@sun-970 sun-970 commented Sep 20, 2026 •

Copy link
Copy Markdown
Owner

Closed: used only to import upstream objects for Git Data API.

PeterGuy326 and others added 30 commits September 1, 2026 13:49
## Summary

- move GitHub repository, issue, badge, contribution, Release, and
raw-content coordinates to `bytefolk`
- update the npm bootstrapper default Release repository and its
regression coverage
- preserve `@fullstack-ai-infra/mem-mcp`,
`io.github.fullstack-ai-infra/mem-mcp`, and existing cache paths

## Cutover gate

This PR prepares the repository for the organization handle cutover
tracked by bytefolk/.github#20. Do
not merge before the organization rename window.

## Validation

- `cd npm && npm test` — 34 passed, 1 Windows-only test skipped on macOS
- `bash scripts/validate_release_version.sh 0.1.1` — passed
- `git diff --check` — passed
- old GitHub URL/API/raw/SSH owner-coordinate scan — 0 matches
- stable npm scope, MCP identity, and cache paths — retained

## Rollback

Before cutover, revert this commit if the rename window is cancelled.
After cutover, GitHub redirects provide a safety net, while the
canonical coordinates in this PR should remain.

Refs bytefolk/.github#20
#152)

Adds an `npm-publish` job to release.yml implementing G4 via npm OIDC
Trusted Publishing (founder 08-30 ruling).

- `permissions: contents: read` + `id-token: write`
- `cd npm && npm publish --provenance --access public`
- No `NPM_TOKEN` / `NODE_AUTH_TOKEN` — auth is exchanged from the GitHub
OIDC id-token against the npmjs Trusted Publisher
(org=fullstack-ai-infra / repo=mem / workflow=release.yml), which the
founder configures on npmjs.com.
- checkout pinned to the same SHA as existing steps; node 22; `needs:
[release]` so it runs after the GitHub Release (G1) exists and
install.js can pull binaries.

Dependency order held: G1 (v0.1.0 GitHub Release) first; install.js
pulls binaries from the Release at install time.

Refs #104
Bumps [browserslist](https://github.com/browserslist/browserslist) from
4.28.2 to 4.28.8.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/browserslist/browserslist/releases">browserslist's
releases</a>.</em></p>
<blockquote>
<h2>4.28.8</h2>
<ul>
<li>Fixed <code>including kaios</code> in baseline queries (by <a
href="https://github.com/Jaybhade"><code>@​Jaybhade</code></a>).</li>
</ul>
<h2>4.28.7</h2>
<ul>
<li>Improved parsing performance.</li>
<li>Fixed unbounded memory growth (by <a
href="https://github.com/alanturing881"><code>@​alanturing881</code></a>).</li>
<li>Fixed prototype write issue (by <a
href="https://github.com/alanturing881"><code>@​alanturing881</code></a>).</li>
</ul>
<h2>4.28.6</h2>
<ul>
<li>Fixed Electron version queries (by <a
href="https://github.com/spokodev"><code>@​spokodev</code></a>).</li>
</ul>
<h2>4.28.5</h2>
<ul>
<li>Fixed <code>&gt;</code> and <code>&gt;=</code> queries (by <a
href="https://github.com/spokodev"><code>@​spokodev</code></a>).</li>
</ul>
<h2>4.28.4</h2>
<ul>
<li>Fixed <code>SyntaxError</code> regression of 4.28.3.</li>
</ul>
<h2>4.28.3</h2>
<ul>
<li>Fixed baseline query case-insensitivity (by <a
href="https://github.com/swwind"><code>@​swwind</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md">browserslist's
changelog</a>.</em></p>
<blockquote>
<h2>4.28.8</h2>
<ul>
<li>Fixed <code>including kaios</code> in baseline queries (by <a
href="https://github.com/Jaybhade"><code>@​Jaybhade</code></a>).</li>
</ul>
<h2>4.28.7</h2>
<ul>
<li>Improved parsing performance.</li>
<li>Fixed unbounded memory growth (by <a
href="https://github.com/alanturing881"><code>@​alanturing881</code></a>).</li>
<li>Fixed prototype write issue (by <a
href="https://github.com/alanturing881"><code>@​alanturing881</code></a>).</li>
</ul>
<h2>4.28.6</h2>
<ul>
<li>Fixed Electron version queries (by <a
href="https://github.com/spokodev"><code>@​spokodev</code></a>).</li>
</ul>
<h2>4.28.5</h2>
<ul>
<li>Fixed <code>&gt;</code> and <code>&gt;=</code> queries (by <a
href="https://github.com/spokodev"><code>@​spokodev</code></a>).</li>
</ul>
<h2>4.28.4</h2>
<ul>
<li>Fixed <code>SyntaxError</code> regression of 4.28.3.</li>
</ul>
<h2>4.28.3</h2>
<ul>
<li>Fixed baseline query case-insensitivity (by <a
href="https://github.com/swwind"><code>@​swwind</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/browserslist/browserslist/commit/f2f2e6cfb01bb4942941d328737546f4e2ae41ad"><code>f2f2e6c</code></a>
Release 4.28.8 version</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/d0787c88fa29ba895fea51cfe921232c7b5d1377"><code>d0787c8</code></a>
Update dependencies</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/fcf8fa9857b30ccdf801a548f5d09d3c4ff0d43f"><code>fcf8fa9</code></a>
Merge pull request <a
href="https://redirect.github.com/browserslist/browserslist/issues/939">#939</a>
from Jaybhade/fix/baseline-kaios-without-downstream</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/57ecd64454e9252afdd6a7e76926e13dda48a38c"><code>57ecd64</code></a>
fix: support &quot;including kaios&quot; without downstream</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/093a0f67bb0becda55235d767b134df3197c54a1"><code>093a0f6</code></a>
Update EM banner</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/b637868045806d2fba4c24eb0060e4cc8b1db276"><code>b637868</code></a>
Release 4.28.7 version</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/313f4659b9f985ade89d1d6a54a860371c41cc46"><code>313f465</code></a>
Update dependencies</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/c935c5a206f8b13db8846818bc03643e147dcbdf"><code>c935c5a</code></a>
Fix regexp performance</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/d7e9e653cb53399065943f59f0b3063987b0a008"><code>d7e9e65</code></a>
Rewrite structure parsing to make it always fast</li>
<li><a
href="https://github.com/browserslist/browserslist/commit/ec4a55efd76bdfa506ec7ce4fea1691559e9ca8f"><code>ec4a55e</code></a>
Fix import order</li>
<li>Additional commits viewable in <a
href="https://github.com/browserslist/browserslist/compare/4.28.2...4.28.8">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for browserslist since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=browserslist&package-manager=npm_and_yarn&previous-version=4.28.2&new-version=4.28.8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/bytefolk/mem/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 勒布朗-詹姆斯 <2986253039@qq.com>
## Tracking record

Refs #124

- Consumed revision: [#124
R4](#124 (comment))
- This candidate does not close #124. Merge-SHA evidence and product
acceptance remain separate.

## Bounded change

The exact diff against main changes one global CODEOWNERS line:

```diff
-* @PeterGuy326 @Bindy-lbb
+* @PeterGuy326 @Bindy-lbb @waterbro-8
```

No collaborator role, branch-protection setting, required-review count,
administrator bypass, tag ruleset, tag permission, release, or
repository setting changes.

## Requirement trace

| Requirement / acceptance | Evidence |
| --- | --- |
| REQ-001; AC-001 | Exact one-line .github/CODEOWNERS diff from base
cc727db to head
474fb33. |
| REQ-002; AC-002 | Before-state GitHub protection readback confirms
strict checks, code-owner review, stale-review dismissal, last-push
approval, linear history, admin enforcement, force-push prohibition, and
deletion prohibition remain enabled. Exact-head CI is required. |
| REQ-003; AC-003 | Diff contains no tag or collaborator configuration;
an independent current-head CODEOWNER review remains required. |

## Validation

- git diff --check: passed.
- git diff --name-only origin/main...HEAD: only .github/CODEOWNERS.
- Git author and committer: PeterGuy326 using
47820304+PeterGuy326@users.noreply.github.com.
- Sensitive-content scan: no credentials or private data introduced.

## Review and delivery gates

This clean candidate supersedes
[#132](#132) because that branch has
public commits with non-noreply personal metadata. #132 is left
unchanged for audit and is not force-pushed or merged.

@Bindy-lbb must provide a current-head independent CODEOWNER approval
after all required CI is green. The author and last pusher will not
self-approve. Normal merge authorization, merge-SHA push/main checks,
verification ledger, and product acceptance still apply.

Co-authored-by: Bindy <70745012+Bindy-lbb@users.noreply.github.com>
Co-authored-by: 勒布朗-詹姆斯 <2986253039@qq.com>
… headers (#161)

## What this changes

Implements the security-header split decided for this repository on
2026-09-03: **nginx is the single authority for
`X-Content-Type-Options`, `X-Frame-Options` and `Referrer-Policy`,
uniformly `no-referrer`, with the proxied path de-duplicated by
`proxy_hide_header`; the API keeps `Content-Security-Policy`,
`X-XSS-Protection` and `Content-Disposition`.**

Closes #135 (the two live proxy defects) and Closes #136 (the ownership
question that defect raised).

This is the in-repo redo of #144, which came from a fork and is being
closed under the fork-policy decision. It is not a cherry-pick of that
commit — no commit from it is imported here.

### The two defects, as measured

Both were real on `main@1332bf4` and neither is visible by reading the
config, which is why the test starts an nginx rather than grepping one.

1. **`/assets/` lost all three headers.** An `add_header` inside a
location replaces the inherited set instead of adding to it, and that
block has always had `add_header Cache-Control`. Every cached bundle
shipped with no `nosniff`, no `X-Frame-Options` and no `Referrer-Policy`
at all.
2. **`/v1/` sent two conflicting `Referrer-Policy` values on one
response.** The proxy said `same-origin`, the API says `no-referrer`,
and nginx's `add_header` appends rather than replaces, so a client
received both and the effective policy depended on which one the browser
kept. `X-Content-Type-Options` and `X-Frame-Options` were also
duplicated (same value twice).

### Why the API keeps sending the three it no longer owns

`proxy_hide_header` makes the wire value nginx's, which is what "nginx
独占" requires, without deleting `nosniff`/`DENY`/`no-referrer` from
`securityHeadersMiddleware`. A `memd` reached directly — the Helm path
exposes the Service, and `docs/DEPLOYMENT.md` only makes the nginx
guarantee for the container — keeps its defense in depth. If the
intended reading was instead that the Go middleware should stop setting
them, that is a one-line change here and it should be said before merge,
because the alternative silently weakens the no-proxy deployment.

## Test evidence

`scripts/test_nginx_security_headers.sh` renders the shipped template
with the same `envsubst` filter and variables the container entrypoint
uses, runs a fake upstream that answers exactly like
`securityHeadersMiddleware` does, starts nginx against the config, and
reads headers off the wire across five surfaces (`/`, `/assets/`, a 404
under `/assets/`, `/v1/`, `/healthz`).

| run | result |
| --- | --- |
| template as shipped on `main` | **11 of 28 assertions fail** — 6
missing across the two `/assets/` surfaces, `same-origin` on `/` and
`/healthz`, 3 duplicated on `/v1/` |
| this branch | **28 of 28 pass** |
| drop the `/assets/` restatement | 3 fail (`/assets/` 200) |
| drop `always` from the `/assets/` restatement | 3 fail (`/assets/` 404
only) |
| drop `proxy_hide_header` | 3 fail (`/v1/` duplicates) |

Each of the three fix sites is therefore individually load-bearing, and
the 404 surface earns its place: it is the only one that tests `always`.

The harness also refuses to report success on a partial run (the
assertion count is pinned), hard-fails when a caller names an
`NGINX_BIN` that is not executable, and only reports `SKIP` when no
nginx was found at all — so the CI leg cannot go green by measuring
nothing.

Executed locally against nginx **1.27.4**, the same minor the
`web/Dockerfile` pins (`nginxinc/nginx-unprivileged:1.27.4-alpine3.21`).

**Measured in CI on the built image, and it did go red there first.**
The first run of the new step failed exactly where this paragraph
expected the risk to be: `web/Dockerfile:17` ends on
`nginxinc/nginx-unprivileged`, whose own build stops at `USER 101`, so
`apk add` could not write the package database -- `ERROR: Unable to lock
database: Permission denied`, `exit code: 99`. Commit `1c917f96`
installs the four tools in a throwaway child image that returns to uid
101 afterwards, so the harness still runs unprivileged, as the shipped
container does. On that head `Deployment profiles` is green and its job
log carries `all 28 security-header assertions passed`, so the leg
executed the contract against the nginx that ships rather than printing
`SKIP`. The harness content measured above is unchanged by that commit;
it touches only the workflow step.

## Not in scope here, and worth a decision

Nothing in this repository sets a `Content-Security-Policy` for the SPA
itself — `default-src 'none'` is an API-only value and would break the
web app if applied to it. The decided split covers the three shared
headers and leaves HTML/SPA CSP unowned. This PR deliberately does not
invent one.

## Links and state

- `Refs` the decided split; `Closes #135` / `Closes #136` on merge.
- No Go code changed, so the existing `security_headers_test.go` and
`content_*` tests are unaffected.
- `Web` is red on this head and identically red on `main` itself (`Audit
dependencies`, `browserslist <=4.28.6`, GHSA-73wf-gq98-2v4g). That is a
pre-existing repository condition rather than something this branch
introduced, and the queued fix is #159; the readback is on #138.
- Opening this PR is not a claim that it should be merged. Independent
review is the reviewers'; I have not requested or recorded one.

---------

Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
## Tracking record

Refs #133

- Consumed revision: [#133
R1](#133 (comment))
- This PR does not close the Issue. Product acceptance remains a
separate step after merge evidence.

## Why

A concurrent cold-cache install on Windows can surface mkdir contention
as EPERM or EACCES rather than EEXIST. The installer previously treated
only EEXIST as retryable. The original remediation candidate
[#134](#134) also left a P1 retry
path: an EEXIST to ENOENT or stale-rename race could retry synchronously
before its deadline or abort-aware delay.

This is a clean successor to #134. That older public branch is left
intact for audit; no history is rewritten or force-pushed.

## Scope

- Classify EEXIST as lock contention on every platform and EPERM or
EACCES as potential contention only on win32.
- Prove a lock can be inspected before retrying a Windows
permission-shaped contention error; unproven permission failures still
fail closed.
- Route every failed acquisition, including stale recovery and
lock-disappearance races, through the existing deadline and abort-aware
poll.
- Add deterministic regression coverage and the Unreleased changelog
entry.

No lock primitive, per-asset ownership model, stale or orphan threshold,
dependency, public API, tag, npm publication, or release behavior
changes.

## Requirement trace

| Requirement / acceptance | Implementation | Evidence |
| --- | --- | --- |
| REQ-001; AC-001, AC-002 | npm/install.js error classifier and
acquireAssetLock | Host-independent classifier table and real-lock
simulated Windows EPERM acquisition test. |
| REQ-002; AC-003 | npm/install.js lock inspection path | Persistent
no-lock EPERM and inspection EACCES tests reject promptly. |
| REQ-003; AC-004 | npm/install.js platform guard | Linux and Darwin
keep EPERM or EACCES fatal; exact-head Windows CI remains required. |
| REQ-004; AC-001, AC-004 | injectable osPlatform plus
npm/install.test.js | win32 branch is exercised off Windows; hosted
node24-windows job is required before review. |
| AC-005 | CHANGELOG.md | Unreleased user-visible fix note. |

## Failure evidence and regression proof

At base cc727db, a deterministic
pre-require fs injection of EEXIST followed by lstat ENOENT produced two
immediate lock mkdir attempts and an EPERM sentinel: the retry bypassed
its zero wait deadline. This candidate makes the same path return the
expected timeout after one attempt. A separate stale rename ENOENT race
has the same bounded behavior.

## Validation

Local macOS, Node 24:

| Command | Observed result |
| --- | --- |
| PATH=/Users/huyz/.nvm/versions/node/v24.14.1/bin:$PATH npm test in
npm/ | 41 passed, 0 failed, 1 Windows-only shim skipped as expected on
macOS |
| node --test npm/install.test.js | 30 passed, 0 failed |
| node --check install.js; node --check mem-mcp; node --check
platforms.js | passed |
| npm pack --dry-run --ignore-scripts | passed; six expected package
files only |
| git diff --check | passed |
| focused added-diff sensitive-pattern scan | no matches |

Independent preflight replayed the focused and full Node 24 suite, plus
the full Node 18 suite: 41 passed, 0 failed, 1 expected Windows-only
skip in each full run. It found no P0 or P1 and recorded PREFLIGHT PASS
for this code candidate.

## Review and delivery gates

- Exact head required CI, especially npm wrapper compatibility
(node24-windows), must be green.
- @Bindy-lbb must provide an independent current-head CODEOWNER
approval. The author and last pusher will not self-approve.
- Normal merge authorization, merge-SHA push/main verification,
verification ledger, and product acceptance remain required.

## Security, compatibility, and release boundary

The retry set is deliberately narrow. On Windows, EPERM or EACCES is
retried only after a lock can be inspected; otherwise the original error
is surfaced. Non-Windows behavior remains fail-closed. No credentials,
dependencies, or public-history changes are introduced.

v0.1.1 was already published from main. This candidate may only enter a
later, separately authorized release; it does not authorize a tag move,
npm publish, GitHub Release, merge, or Issue closure.

Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
Queued for protected squash merge after independent Code Owner approval and green security checks.
## Requirement / goal

Refs #139 (CI baseline only; this PR does not expand the checkpoint bug
scope). Restore the current `mem` Web audit gate by refreshing the
lockfile entries with published fixes.

## Scope

- Refresh only `web/package-lock.json`.
- Resolve the current main Web audit findings for js-yaml,
Vitest/@vitest/mocker, and postcss-selector-parser.
- No application code, server behavior, transcript format, or checkpoint
implementation changes.

## Validation ledger

- PASS — `npm ci`
- PASS — `npm run audit` (production and high-severity development
gates)
- PASS — `npm run lint`
- PASS — `npm run typecheck`
- PASS — `npm run build`
- PASS — `git diff --check`

## Known limitations

- This is a CI/security-baseline prerequisite for clean current-main PR
validation, not the #139 product fix and not an issue-close claim.
- The lockfile refresh is intentionally separate from
[#191](#191); #191 should be rebased
or retargeted after this baseline lands.
)

## What this changes

`mcpName` becomes **`io.github.bytefolk/mem-mcp`** in `npm/server.json`
and `npm/package.json`. This implements the registry half of the
2026-09-03 decision that G5 does not wait on an npm scope migration.

The official MCP Registry derives an entry's namespace from the
**repository owner**. The value on `main` still names
`fullstack-ai-infra`, which this repository no longer is, so a
submission carrying it either points into a namespace we do not control
or is rejected by namespace validation. That single stale string is why
the registry listing never appeared — reading
`search=io.github.fullstack-ai-infra` and `search=bytefolk` against the
public registry API both return `count: 0`, so there is no prior record
to migrate over.

## What is deliberately not changed

| | value | why it stays |
| --- | --- | --- |
| npm package name | `@fullstack-ai-infra/mem-mcp` | The decision keeps
the published scope. A registry identifier and an npm package identifier
are different namespaces; moving the package would break every existing
`"args": ["-y", "@fullstack-ai-infra/mem-mcp"]` client config and every
cached install for no registry-side gain. |
| installer cache directory | `…/fullstack-ai-infra/mem-mcp` | Renaming
it discards working caches to change a folder name. |
| version | `0.1.1` | `scripts/validate_release_version.sh:39` pins
`npm/server.json`'s version to the tag, so the bump belongs to the
release commit, not here. |

The resulting shape — npm scope `@fullstack-ai-infra`, registry
namespace `io.github.bytefolk` — is intentional and is what the new test
encodes, so it does not read as a half-finished migration.

## The guard

The defect was a stale string in a manifest, and a rename is precisely
the event that makes a manifest stale, so
`npm/registry-identity.test.js` now asserts the identifier against the
repository coordinate `npm/install.js` already downloads from. It
derives the expected namespace rather than hardcoding `bytefolk`, so a
future rename moves the assertion instead of breaking it.

| run | result |
| --- | --- |
| this branch | 3/3 pass |
| `mcpName` left on `fullstack-ai-infra` (the shipped state) | 2 of 3
fail |
| repository coordinate renamed, identifier not followed | 1 of 3 fails
|
| full `npm test` | 38 tests, 37 pass, 1 pre-existing platform skip |
| same on `main@1332bf4` | 35 tests, 34 pass, 1 skip |

Added to the `test` script's file list, so it runs in the `npm-wrapper`
CI job rather than only when named directly.

## Two things to know before reviewing

- **`npm run test:tarball` fails on this branch and identically on
`main`.** It is an `npm install --offline` of the locally packed tarball
against this host's npm cache, not something this change touched. CI
invokes it as `npx --yes npm@12.0.2 run test:tarball`, which this host
cannot reproduce, so treat that leg as CI-covered only.
- **#153 now contradicts this PR.** That issue asks to migrate the
package to `@bytefolk/mem-mcp`; the decision is to keep the scope. I
linked with `Refs #153`, not `Closes`, because only the registry half is
done here. #153 should be rewritten, or the next person to pick it up
will move the scope and undo the part that was never in question.

## Not in this PR

Running `mcp-publisher` and the OAuth / organization verification are
assigned to @PeterGuy326, and the registry record is created by that
submission, not by merging this. `README.md:11` also still points its
Smithery badge at `@fullstack-ai-infra/mem-mcp`; that slug needs
verifying against what Smithery actually resolves before it is edited,
so it is left alone here rather than repointed to something possibly
equally wrong.

Merging this PR is not a claim that it is merge-ready, and no review has
been requested or recorded.

Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
Co-authored-by: 修雨 <47820304+PeterGuy326@users.noreply.github.com>
…/web (#160)

Bumps
[postcss-selector-parser](https://github.com/postcss/postcss-selector-parser)
from 6.1.2 to 6.1.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss-selector-parser/releases">postcss-selector-parser's
releases</a>.</em></p>
<blockquote>
<h2>6.1.4</h2>
<ul>
<li>fix: tolerate non-node children when serializing selectors</li>
</ul>
<h2>6.1.3</h2>
<ul>
<li>Fix <a
href="https://github.com/advisories/GHSA-w9m9-85wc-3x92">CVE-2026-9358</a>
(NVD) / SNYK-JS-POSTCSSSELECTORPARSER-16873882 via backport of (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/pull/316">#316</a>
by <a href="https://github.com/MoOx"><code>@​MoOx</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss-selector-parser/blob/main/CHANGELOG.md">postcss-selector-parser's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog of <code>postcss-selector-parser</code></h1>
<h2>7.1.5 - 2026-08-07</h2>
<ul>
<li>fix: don't treat a non-prefix token before <code>|</code> as a
namespace (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/pull/324">#324</a>
by <a
href="https://github.com/spokodev"><code>@​spokodev</code></a>)</li>
<li>fix: preserve whitespace before a <code>*</code> namespace in
attribute selectors (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/pull/325">#325</a>
by <a
href="https://github.com/spokodev"><code>@​spokodev</code></a>)</li>
<li>fix: TypeError on unclosed <code>[</code>, <code>(</code> and
trailing <code>|</code> (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/pull/330">#330</a>
by <a
href="https://github.com/theRizwan"><code>@​theRizwan</code></a>)</li>
</ul>
<h2>7.1.4 - 2026-06-11</h2>
<ul>
<li>fix: tolerate non-node children when serializing selectors</li>
</ul>
<h2>7.1.3 - 2026-06-11</h2>
<ul>
<li>Improve fix CVE-2026-9358 (NVD) /
SNYK-JS-POSTCSSSELECTORPARSER-16873882 (clone/walk)</li>
</ul>
<h2>7.1.2 - 2026-06-09</h2>
<ul>
<li>Fix <a
href="https://github.com/advisories/GHSA-w9m9-85wc-3x92">CVE-2026-9358</a>
(NVD) / SNYK-JS-POSTCSSSELECTORPARSER-16873882 (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/pull/316">#316</a>
by <a href="https://github.com/MoOx"><code>@​MoOx</code></a>)</li>
</ul>
<h2>7.1.1</h2>
<ul>
<li>perf: replace startsWith with strict equality (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/issues/308">#308</a>)</li>
<li>fix(types): add walkUniversal declaration (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/issues/311">#311</a>)</li>
</ul>
<h2>7.1.0</h2>
<ul>
<li>feat: insert(Before|After) support multiple new node</li>
</ul>
<h2>7.0.0</h2>
<ul>
<li>Feat: make insertions during iteration safe (major)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/4a7e4e3685db8ab8e52e51ecdbe8162a8568f70c"><code>4a7e4e3</code></a>
7.1.4</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/e2021c523d5ef1bf27836aef3bd724a28ba7894f"><code>e2021c5</code></a>
fix: tolerate non-node children when serializing selectors</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/7893b741fa87d0401e39c3a7f00d89cf7408ad32"><code>7893b74</code></a>
7.1.3</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/5bc698cef66f8abd12610dc623e5d67cbc0f869d"><code>5bc698c</code></a>
Improve fix CVE-2026-9358 (NVD) / SNYK-JS-POSTCSSSELECTORPARSER-16873882
(clo...</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/db3232710d7270b34e50b4ffae493ac19204f570"><code>db32327</code></a>
run oxfmt</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/16e2581b40894504cf2b979fc0ebf7d0b2f39366"><code>16e2581</code></a>
simplify deps (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/issues/319">#319</a>)</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/b185e2057871669f9c571ad82e4350799e0a2329"><code>b185e20</code></a>
Add description in package.json + full repo url</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/de415f19aac008f0e731590222f3a963193be05c"><code>de415f1</code></a>
Add Tidelift security notice</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/c4f2c8c04a8107e4e401f257ef00592b59633774"><code>c4f2c8c</code></a>
CI: make Node 14 test job lockfile-v3 compatible (<a
href="https://redirect.github.com/postcss/postcss-selector-parser/issues/318">#318</a>)</li>
<li><a
href="https://github.com/postcss/postcss-selector-parser/commit/4e93663bfe861f9fc3173db603c8fadb70f8090a"><code>4e93663</code></a>
Fix test run on node &lt; 20</li>
<li>Additional commits viewable in <a
href="https://github.com/postcss/postcss-selector-parser/compare/v6.1.2...6.1.4">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~moox">moox</a>, a new releaser for
postcss-selector-parser since your current version.</p>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 勒布朗-詹姆斯 <2986253039@qq.com>
Co-authored-by: 修雨 <47820304+PeterGuy326@users.noreply.github.com>
…165)

Refs #112

## Summary

A configured URL that carries credentials in a shape `url.Parse` does
not report
as **userinfo** reaches output today. This adds one shared gate that
redacts such
a value when it can prove it is a transport URL, and **withholds the
value whole**
when it cannot, and wires it into all three egresses R3 names: the API
client,
`memd`'s log lines, and `mem doctor`.

The adjudicated direction this implements is recorded on #112 as R3
(comment 5536659086): gate on *"parses as a recognized transport scheme
with
userinfo redacted, otherwise withhold the whole value"*.

## Scope: all three named egresses

R3 names three egresses — doctor, apiclient, `memd`'s log line. All
three are
covered here.

| egress | on `main` at the base | covered here |
| --- | --- | --- |
| apiclient — request construction | leaks | gated |
| apiclient — all 4 `http.Client.Do` sites | leaks, ungated entirely |
gated, one test case per site |
| apiclient — `newRequest` on the workspace-transfer path | leaks |
gated (arrived with #164's `workspace_transfer.go`) |
| `memd` startup log line | leaks (`User != nil` gate) | gated |
| `memd` fatal log line | leaks via a third-party error | gated |
| `mem doctor` (text + JSON) | not present | gated through the same
package |

### How doctor got onto this branch

`mem doctor` is not on `main`; it only existed on #164 (which sits on
#131's
original doctor commit). Because #131 was closed as superseded by #164,
doctor
had exactly one carrier, so the surviving branch had to absorb it before
#164
could be closed. Rather than re-author it, this branch **merges** the
work:

```
c9e0e63  docs: correct doctor's stated secret guarantee                        (this change)
d30f1ff  fix(cli): route mem doctor's URL reporting through the shared gate   (this change)
ad4e78f  Merge origin/main
d7f2dcb  Merge #164 (mem doctor) into the credential-url-gate branch
b229537  fix(client,memd): withhold URLs whose credentials cannot be attributed
fffcd4c  fix(cli): redact malformed URLs and request build errors   ← #164, author PeterGuy326
46499b2  feat(cli): add mem doctor and first-run guidance           ← #131, author waterbro-8
```

Both earlier commits are **ancestors** of this head, so #164's and
#131's authorship
is preserved on the merge rather than claimed as mine. #164's unrelated
per-probe `--timeout` fix and its `workspace_transfer.go` coverage came
across
with the merge and are kept.

What `d30f1ff` itself does is replace doctor's two local string-level
helpers
with calls into `internal/redact`, so there is one policy rather than
three:

```go
func redactURL(raw string) string          { return redact.URL(raw, redact.APIURLs) }
func sanitizeProbeError(err error) string  { return redact.TransportError(err, redact.APIURLs) }
```

`c9e0e63` is prose only. It corrects three statements — in
`CHANGELOG.md`,
`docs/DEPLOYMENT.md` and a comment in `cmds_doctor.go` — that asserted
the doctor
report contains **no secret value**. That was never true of the
query-parameter
shape described under Known gap, measured at 3 sentinel occurrences per
output
format on this head. The non-goal stands; the promise was the thing that
was
wrong, so the docs now name the residual instead of denying it.

## The shape, and why the previous gate missed it

```
url.Parse("admin:pw@host")
→ Scheme="admin"  Opaque="pw@host"  User=nil
```

A gate written as `if parsed.User == nil { return raw }` therefore
echoes the
credential verbatim — not as a corner case, but as the normal path for
any
value whose scheme happens to contain a colon.

The gate also refuses to scrub error *text*, because that cannot be made
tight:
`url.Error` renders with `%q`, so a `"` inside a password arrives
escaped and a
scanner that pairs quotes mis-pairs, replaces nothing, and leaves its
cursor
inside the URL. Withholding is chosen over partial trimming for the same
reason:
a delimiter inside a credential splits the message into pieces that no
longer
look like a URL, and the piece without the `@` is exactly the half that
leaked.

`memd`'s fatal line is included because `queue.NewClient` wraps asynq's
parse
error, which embeds the whole DSN:

```
queue: parse redis url: asynq: could not parse redis uri:
  parse "redis://:PASS@ho st:6379/0": invalid character " " in host name
```

slog renders an error value as its text, so that reaches the log
unmodified.

## Known gap, stated rather than smoothed over

The gate proves the absence of **userinfo**, not of every credential. A
secret
supplied as a query parameter (`redis://host:6379/?password=x`) parses
as a
clean URL with `User == nil` and **is still echoed**. This is the
adjudicated
scope, not an oversight, and closing it is a separate decision. It is
pinned by
a named characterization test
(`TestTextKnownGapQueryParameterCredentialsAreEchoed`) that fails if
someone
closes the gap without updating the expectation, so the residual cannot
become
folklore.

Withholding also has a real diagnosability cost, accepted by design: the
reason
a request failed is still reported, the host is not.

```
Error: Get [withheld]: unsupported protocol scheme "admin"
```

## Validation ledger

Evidence level claimed: **E3 — independently reproduced**. Not E4: I am
the
author, and E4 requires a non-author to verify the acceptance criteria
end to
end.

Environment: Linux x64, Go 1.25.0, exact tree
`9fa9c14de074b4ddb5320d4f83803f1657918b34` — `HEAD^{tree}` of the
current head
`c9e0e63`, read back from `GET /repos/bytefolk/mem/commits/c9e0e63…` and
equal to
it byte-for-byte, so the tree measured here and the tree on the PR are
the same
object. (This head was pushed through the Git Data API because
`github.com:443`
was down on this box; the local commit sha differs from the remote one,
the tree
does not.)

| Check | Result |
| --- | --- |
| `go build ./...` | pass |
| `go vet ./...` | pass |
| `gofmt -l .` | no output |
| `go test -count=1 ./...` | 32 packages ok, 0 FAIL |
| `cmd/mem` (includes 16 `mem doctor` tests) | 102 tests, 0 fail |
| `internal/redact` | 30 tests, 0 fail |
| `internal/apiclient` | 45 tests, 0 fail |
| `cmd/memd` | 15 tests, 0 fail |
| `git merge-base --is-ancestor origin/main HEAD` | yes (main not
silently reverted) |
| CI on this head | **15/15 check names `success`** on `c9e0e63`
(`run_attempt` 1), including `Go` and `PostgreSQL integration`; also
15/15 on the immediately preceding `d30f1ff` |

### `mem doctor` end-to-end, measured on built binaries

Seven malformed credential URL shapes, each run through
`mem doctor --server <shape> --timeout 1s` in **both** `text` and
`json`,
counting occurrences of a sentinel password in everything the process
wrote:

| # | shape | #164 head `fffcd4c` | this head `c9e0e63` |
| --- | --- | --- | --- |
| 1 | `http://admin:PW@127.0.0.1:1` | 0 | 0 |
| 2 | `http://admin:PW@127.0.0.1:%zz` | 0 | 0 |
| 3 | `http://admin:PW@ho st.example.com` | 0 | 0 |
| 4 | `http://admin:PW x@127.0.0.1:1` | 0 | 0 |
| 5 | `http://admin:PW@%` | 0 | 0 |
| 6 | `http://admin:PW@mem.internal:99999999` | 0 | 0 |
| 7 | `admin:PW@mem.internal:8787` (no scheme) | **6** | **0** |

Shape 7 is the `url.Parse` → `User=nil` case from the section above:
#164
prints the configured URL three times (`server`, `detail`, `hint`) in
each of
the two formats, so 6 occurrences is one leak per field.

**A zero from a binary that has no `doctor` command means nothing.**
This table
was first produced against a build of `b229537` and came back
0/0/0/0/0/0/0,
which looked like a pass — the actual output was
`Error: unknown command "doctor" for "mem"`, because that head had no
doctor at
all. The harness now counts `unknown command` alongside `unknown flag`
and every
row above was taken with both counters at 0.

### Mutation controls

Run against a copy, with the mutation confirmed present in the source
before the
suite is trusted:

1. Reverting the gate to the pre-fix `User != nil` form turns **10 named
cases
   red across 3 packages** (`internal/redact`, `cmd/memd`, `cmd/mem`).
2. Removing the gate at **one** apiclient `Do` site turns red **exactly
that
site's** test case and leaves the other three green. The four cases map
one-to-one onto the four sites, so a regression at a single site cannot
hide.
Control 2 was found by this method — an earlier version of this port
left one
   site raw and the table caught it.
3. Putting **#164's original `redactURL` / `sanitizeProbeError` back**
into this
tree — i.e. merging #164 and *not* rewiring doctor — fails **exactly one
test**, `TestDoctorSchemelessServerURLDoesNotLeakCredentials`, and its
built
binary leaks 2 sentinel occurrences on shape 7. Before that test was
added,
   the same mutation failed **zero** tests. So the merge on its own was
   unprotected, and the one new test is what closes that.

### Two pre-existing assertions were relaxed — named, because one is not
mine

The gate withholds a value it cannot prove is a transport URL, where
#164's
local helper echoed a partially cleaned one. Two assertions demanded the
echo
shape specifically, so they were widened to accept **redact *or*
withhold**:

| location | test | author of the assertion as written |
| --- | --- | --- |
| `cmds_doctor_test.go:418` | `TestRedactURLStripsUserinfo` | waterbro-8
(#131) |
| `cmds_doctor_test.go:456` |
`TestDoctorMalformedServerURLDoesNotLeakCredentials` | **PeterGuy326
(#164)** |

Both keep their original first clause — the secret must not appear, and
a
cleaned URL must still carry `REDACTED@<host>`. Only the *form* of an
acceptable answer widened. Concretely at `:418`, `strings.Contains(got,
secret)`
still fails the test, and the `REDACTED@mem.internal:8787` check two
lines above
it is untouched.

**No assertion was deleted, and the count went up, not down**: #164's 15
doctor
tests are all present on this head (0 dropped), plus
`TestRedactURLStripsUserinfo`
and the new `TestDoctorSchemelessServerURLDoesNotLeakCredentials` =
**16**. The
way to check this claim is mutation control 3 above: if the widening had
quietly
weakened coverage, restoring #164's scrubber would have gone green, and
instead
it fails a test.

Anyone who considers the withhold form unacceptable for `:456` should
say so on
#164's thread rather than assume I settled it unilaterally — I changed
an
assertion another author wrote to pass my gate.

## What I did not verify

- The leak table was rebuilt and re-run against `c9e0e63` after the docs
commit;
that commit is prose and comments only, and the result is unchanged (0
on all
seven shapes). Earlier heads `d30f1ff` and `b229537` each had 15/15 CI
at the
  time they were written.
- **There is no prior CI baseline for the doctor half to compare
against.**
#164's head `fffcd4c` has 0 `check-runs` and no workflow run has ever
been
recorded for it, so doctor has never been green in CI anywhere —
including
here, where it is now inside the `Go` job for the first time. That is a
gain,
not a regression, but it does mean no one has seen this code pass CI
before.
- Nothing was exercised on Windows or macOS. All results are Linux x64.
- `memd` was **not** driven end-to-end to its fatal redis path. Reaching
it
requires a reachable PostgreSQL (the queue client is constructed after
`db
Open`), and the only local server on 5432 is an unrelated instance whose
credentials I do not have — my attempt failed at `db open: ... failed
SASL
  auth` before touching the queue. That egress is therefore evidenced by
executing the gate on the asynq error string **measured from the real
library
  in this tree**, not by a live `memd` run.
- No claim is made about `#112`'s exit-code contract, R2/R3 acceptance,
or
  #131/#164 merge readiness.

## Non-goals

- No change to query-string credential handling (see Known gap).
- No change to `#164`'s or `#131`'s branches; nothing here is pushed to
anybody
else's ref. #164 is pulled *into* this branch by merge, which is why
#164 can
  be closed without losing its work.
- No re-adjudication of R3. Doctor's `--timeout` behaviour, its check
set and
its JSON schema are #164's, unchanged except where they called the old
local
  helpers.

This PR stays `Refs` on #112 rather than `Closes`, because merging it
would not
by itself settle #112's acceptance — that needs a reviewer's decision,
not just
a diff landing.

**Review strength of this PR, stated plainly:** 15/15 checks pass on
`c9e0e63`, and I am the author of `b229537`, `d30f1ff` and `c9e0e63` —
automated checks and
my own sign-off are not a review. What this PR is missing is exactly one
independent human review; nothing else blocks it (`mergeable=true`, no
conflict
with `main`, which is an ancestor of this head). It needs one human
approval,
and I am not that reviewer — on the doctor half especially, since I
edited an
assertion PeterGuy326 wrote.

---------

Co-authored-by: waterbro-8 <waterbro-8@users.noreply.github.com>
Co-authored-by: PeterGuy326 <47820304+PeterGuy326@users.noreply.github.com>
Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
### Not a review vote, and not an acceptance

I am the author of this branch, so under this org's `require_code_owner`
+
`require_last_push_approval` configuration my own ticket cannot be the
one that
clears it. Nothing here asks for a merge, a close, a label, or a tag.
The
commitment is only: here is a change, here is what I measured.

Supersedes nothing by itself: **#156 is a separate, still-valid
Dependabot PR.**
See *Sequencing* below.

### What changes

Two files, +39/−39:

- `worker/pyproject.toml:31` — `"pypdf>=4.0"` → `"pypdf>=6.18.0"`
- `worker/uv.lock` — `pypdf 6.15.0 → 6.18.0`, nothing else moves

That is the whole diff. No test, no docs, no CI file, no `CHANGELOG.md`
entry:
`grep -i 'pypdf\|numpy\|torch\|dependabot' CHANGELOG.md` on `main`
returns
**zero** dependency-bump entries across its 381 lines, and #156 is 2
files too,
so an entry here would be inventing a convention.

### Why the floor is written `>=6.18.0` and not `>=6.16.1`

Three open Dependabot alerts name `pypdf` in this lock — `#71`
(`GHSA-jp53-mhqp-8xcg`, `< 6.16.0`), `#72` (`GHSA-763m-79hh-57f2`, `<
6.16.1`),
`#73` (`GHSA-23w6-3w8w-8484`, `< 6.16.1`). This satisfies all three, the
same as
#156, once it is on the default branch.

It additionally takes two upstream releases that **have no advisory**,
so no
alert will ever schedule them:

- **6.17.0** (2026-09-04) — `Security (SEC): Limit value for Roman
numerals (#4047)`
- **6.18.0** (2026-09-07) — `Security (SEC): Limit allowed length of
indirect object tokens (#4055)`

Re-measured for this PR: `GET /advisories?affects=pypdf` → **43**
entries, all
`type: reviewed`, and the newest `first_patched_version` anywhere in
that set is
**6.16.1**. Both new entries are the same hardening class as the three
above
(bound an unbounded parser input), and the surface is the untrusted one:
`worker/mem_worker/processors/pdf.py:79` runs
`PdfReader(BytesIO(file.data))` on
**user-uploaded** files.

Writing the floor at 6.18.0 rather than letting the lock alone carry it
is the
part that survives a from-scratch resolve. Measured: **uv resolves to
the newest
release satisfying a floor, not to the floor** — `>=6.16.1` re-resolved
today
lands on 6.18.0, and after the next release it would land past it, in a
manifest
whose stated requirement was never 6.18.0.

### Evidence

All of it executed on a tarball of `main @
2986fe3`
whose tree was proved equal to the remote (`.commit.tree.sha`
`f447ca554377e85ba26090d59882b8d4f2b78731` == `git init && git add -A &&
git
write-tree`), with the tool CI pins — uv `0.9.27` (`ci.yml:185-187`) —
in a
sanitized env (no mirror/proxy variables; `uv lock` through a
mirror-configured
uv rewrites thousands of URL lines and its "lock is stale" verdict is
then
worthless).

| # | check | result |
| --- | --- | --- |
| 1 | `uv lock` on the **untouched** tree | **0 diff lines** vs
committed `worker/uv.lock` — the pin is reproducible, so everything
below is about the resolve, not a stale tool |
| 2 | `uv lock` after only the constraint edit | `Updated pypdf v6.15.0
-> v6.18.0`, 76 lock lines |
| 3 | name/version pairs, both locks | 85 packages each, **exactly one**
difference: `pypdf 6.15.0 → 6.18.0` |
| 4 | `uv lock --locked` | `rc=0` — so the Worker leg's `uv sync
--frozen` (`ci.yml:193`, `UV_FROZEN: "1"` at `:170`) accepts it |
| 5 | dist hashes vs the registry | sdist
`ae58b7d93c22c169ffb02c3b06321c45c4f223b4916536568adb57d789d95d01`,
wheel `05b762b77bcb9dcb4a7c91fcf5dded585b25bee7269ab3d3001d7c55fa1b324b`
— byte-identical to `pypi.org/pypi/pypdf/json` |
| 6 | the repo's own fixture (`test_processor_logic.py:419`) extracted
at 6.15.0 / 6.16.1 / 6.18.0 on **Python 3.11** (`ci.yml:182`) |
identical `page_count`, identical `sha256(extracted_text)`
(`46e7c072b2684841…`, 214 chars), `"1800 RMB" in text` true on all
three, malformed input raises the same `PdfStreamError` on all three |
| 7 | 6.18.0's only behavioural change (`DEP: Rework configuration value
handling (#4044)`) | `grep` over the whole tree for
`overwrite_configuration` / `apply_configuration` /
`disable_legacy_handling` / `pypdf.constants` → **0 hits**; `pypdf`
appears in only 3 files (`worker/pyproject.toml`,
`worker/mem_worker/processors/pdf.py`, `scripts/seed_demo_data.sh`) |

Checks 1–4 and 7 are the ones CI cannot shortcut; 6 is the one that says
"the
worker's PDF path behaves the same", and 4 is the one that says "this
lock is
self-consistent". The Worker test leg on the exact head remains the real
proof
of the full pipeline (`test_processor_logic.py:457`) — **that runs in
CI, I did
not run it here**; the local venv has none of the Worker's own
dependencies
installed.

About the diff size: of the 76 changed lock lines, **8 are pypdf**
(specifier,
version, sdist, wheel — each on both sides). The other 68 are uv's
marker
renormalisation: the `python_full_version >= '3.15' and sys_platform ==
'darwin'` fork marker hopping between `torch 2.13.0` and `2.13.0+cpu`
(likewise
`torchvision 0.28.0` / `+cpu`), `python_full_version` narrowing dropping
off
`numpy` / `scipy` / `tifffile`, and the `resolution-markers` list
reordering.
That is not avoidable churn I chose: **a plain `uv lock` on either side
of a pin
costs 0 lines, but any resolve that moves pypdf pays them** — #156's own
`+53/−53` is the same shape. No other package's *version* changes.

### Process: this PR is ahead of its issue's readiness gate

`AGENTS.md` rule 2 says not to implement a material change until its
issue has
acceptance criteria and `status:ready`. Refs #187, which I filed with
the AC
list and reproduction steps for exactly this; it is
`status:needs-triage`, and
`status:ready` is a maintainer's label, not mine to set. So this is a
**draft**,
and undrafting it is the step that should follow that label rather than
precede
it. If triage would rather the change go in behind #156, #187 can simply
wait.

### Sequencing with #156

Both branches touch the same two files, so they will conflict with each
other,
not with `main`.

- If **#156 merges first**: rebase this onto the new `main` — the
manifest line
is a one-token edit and the lock is one `uv lock` with the pinned tool.
I
  offered to do that; it is not something to do unasked.
- If **this merges first**: #156's diff is a strict subset of this one
and its
three alerts are dismissed by this branch instead. **I am not asking for
#156
to be closed** — it has an in-force `APPROVED` from `PeterGuy326` on its
current head and I am not in a position to spend someone else's ticket.
That
call, and the release-cadence call about whether 6.17.0/6.18.0 belong in
this
  repo's Worker at all, is the owner's.

### What I did not do

No review submitted, no vote, no merge, no close, no label change on
anything
except the new issue's own labels, no ref moved on #156, no `Update
branch`
clicked anywhere, no `CHANGELOG.md` edit, no tag, no publish.

Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
Co-authored-by: 修雨 <47820304+PeterGuy326@users.noreply.github.com>
…rver (#158)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from
1.82.1 to 1.83.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/grpc/grpc-go/releases">google.golang.org/grpc's
releases</a>.</em></p>
<blockquote>
<h2>Release 1.83.2</h2>
<h1>Security</h1>
<ul>
<li>server: Reject requests missing both <code>:authority</code> and
<code>Host</code> headers with HTTP 400 and status
<code>Internal</code>. (<a
href="https://redirect.github.com/grpc/grpc-go/pull/9365">grpc/grpc-go#9365</a>)
<ul>
<li>Special Thanks: <a
href="https://github.com/winklemad"><code>@​winklemad</code></a></li>
</ul>
</li>
</ul>
<h2>Release 1.83.1</h2>
<h1>Security</h1>
<ul>
<li>xds/rbac: Fix a bug where nested <code>Principal</code> or
<code>Permission</code> rules with <code>:scheme</code> or
<code>grpc-</code> prefixed header matchers were not rejected, which
could cause DENY rules to fail open. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9258">#9258</a>)
<ul>
<li>Special Thanks: <a
href="https://github.com/nvxbug"><code>@​nvxbug</code></a></li>
</ul>
</li>
<li>xds/rbac: Fix a bug where the <code>host</code> header matcher was
not being replaced with <code>:authority</code> in nested
<code>Principal</code> or <code>Permission</code> rules. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9258">#9258</a>)
<ul>
<li>Special Thanks: <a
href="https://github.com/nvxbug"><code>@​nvxbug</code></a></li>
</ul>
</li>
<li>xds/rbac: Fix a bug where a header matcher whose name was not
lowercase, such as <code>X-Role</code>, matched no header, which could
cause DENY rules to fail open. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9332">#9332</a>)
<ul>
<li>Special Thanks: <a
href="https://github.com/alimony"><code>@​alimony</code></a></li>
</ul>
</li>
<li>xds/rbac: Fix a bug where a <code>:scheme</code> or
<code>grpc-</code> prefixed header matcher was accepted when its name
was not lowercase. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9332">#9332</a>)
<ul>
<li>Special Thanks: <a
href="https://github.com/alimony"><code>@​alimony</code></a></li>
</ul>
</li>
<li>xds/rbac: Fix a bug where a <code>Host</code> header matcher was not
replaced with <code>:authority</code>. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9332">#9332</a>)
<ul>
<li>Special Thanks: <a
href="https://github.com/alimony"><code>@​alimony</code></a></li>
</ul>
</li>
</ul>
<h1>Performance</h1>
<ul>
<li>transport: Restrict memory overhead of buffering small data frames.
(<a
href="https://redirect.github.com/grpc/grpc-go/issues/9331">#9331</a>)</li>
</ul>
<h2>Release 1.83.0</h2>
<h1>Security</h1>
<ul>
<li>server: Stop reading from connections when flooded by HTTP/2 frames
to mitigate resource exhaustion. The default value for this limit is 100
frames, excluding DATA and HEADERS, and may be changed by setting
environment variable
<code>GRPC_GO_EXPERIMENTAL_CONTROL_BUFFER_THROTTLE_LIMIT</code>.</li>
<li>xds/rbac: Support <code>Metadata</code> and
<code>RequestedServerName</code> permissions matcher fields. If present
in a DENY rule, previously these would be ignored and fail-open.</li>
<li>xds/rbac: Fix panic when parsing unsupported fields in
<code>NotRule</code>/<code>NotId</code> permissions.</li>
<li>xds/rbac: Support the deprecated <code>source_ip</code> principal
identifier by treating it as equivalent to
<code>direct_remote_ip</code>.</li>
<li>xds: Fix panic when parsing route header matchers configured with
empty <code>exact_match</code>, <code>prefix_match</code>, or
<code>suffix_match</code> strings. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9223">#9223</a>)</li>
</ul>
<h1>New Features</h1>
<ul>
<li>xds/googlec2p: Enable DirectPath over Interconnect support for
on-premises clients via the <code>force-xds</code> target URI query
parameter. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9133">#9133</a>)</li>
<li>xds: Enable xDS configuration to control which fields get propagated
from ORCA backend metric reports to LRS load reports. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9145">#9145</a>)</li>
<li>authz: Add <code>OnPolicyUpdate</code> callback to
<code>FileWatcherOptions</code> to notify when an authz policy is loaded
or updated. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9142">#9142</a>)
<ul>
<li>Special Thanks: <a
href="https://github.com/hnefatl"><code>@​hnefatl</code></a></li>
</ul>
</li>
<li>xds: Add support for the GCP Authentication HTTP Filter, which
automatically fetches and attaches GCP Service Account Identity JWT
tokens to outgoing RPCs.
<ul>
<li>This feature can be enabled by setting environment variable
<code>GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER=true</code>. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9119">#9119</a>)</li>
</ul>
</li>
<li>xds: Add support for xDS-based HTTP CONNECT proxies.
<ul>
<li>This feature can be enabled by setting environment variable
<code>GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT=true</code>. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9151">#9151</a>)</li>
</ul>
</li>
<li>xds: Add support for <code>contains_match</code> in route header
matchers. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9223">#9223</a>)</li>
</ul>
<h1>Bug Fixes</h1>
<ul>
<li>credentials/alts: Fix panic when processing malformed frames by
validating that the message frame length exceeds the message type field
size. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9197">#9197</a>)</li>
<li>grpc: Fix compilation on Plan 9 targets (<code>GOOS=plan9</code>),
broken since v1.81.0. (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9255">#9255</a>)
<ul>
<li>Special Thanks: <a
href="https://github.com/Yusufihsangorgel"><code>@​Yusufihsangorgel</code></a></li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/grpc/grpc-go/commit/030ee8becb20ce4315d6bf2dfa26bdd876169dc4"><code>030ee8b</code></a>
Update version to 1.83.2 (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9375">#9375</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/8668b69c167df908b6b3666dcbf40992b9e932a4"><code>8668b69</code></a>
cherry-pick <a
href="https://redirect.github.com/grpc/grpc-go/issues/9365">#9365</a> to
v1.83.x (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9366">#9366</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/a3e952d2b7c973b7ec6357676e55a2a9c9faaa0d"><code>a3e952d</code></a>
cherry-pick <a
href="https://redirect.github.com/grpc/grpc-go/issues/9346">#9346</a> to
v1.83.x and update x/net dependency (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9369">#9369</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/58f8fd9a002536548ac96e34621d761b29cc4f3e"><code>58f8fd9</code></a>
Change version to 1.83.2-dev (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9337">#9337</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/1550d9e0cddb30ce99e61a2102e8294a49461e5e"><code>1550d9e</code></a>
Change version to 1.83.1 (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9336">#9336</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/ebba6f3f1b206e2b4dc4d1d5a96d18430302c2fe"><code>ebba6f3</code></a>
Cherry-pick <a
href="https://redirect.github.com/grpc/grpc-go/issues/9258">#9258</a>
and <a
href="https://redirect.github.com/grpc/grpc-go/issues/9332">#9332</a>
into v1.83.x (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9335">#9335</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/8cfeca0e1ee5ea0980dcc320e20240fa1079ec77"><code>8cfeca0</code></a>
Cherry-pick <a
href="https://redirect.github.com/grpc/grpc-go/issues/9331">#9331</a> to
v1.83.x (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9333">#9333</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/dec6951305e88906696f1d0a00dd2439363bc708"><code>dec6951</code></a>
Change version to 1.83.1-dev (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9229">#9229</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/4c226daff88f54441d70f710815e07b81fb162b2"><code>4c226da</code></a>
Change version to 1.83.0 (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9228">#9228</a>)</li>
<li><a
href="https://github.com/grpc/grpc-go/commit/c198988aa9297cb9428c7afaaee4363d0082b838"><code>c198988</code></a>
Cherrypick 9223 into v1.83.x (<a
href="https://redirect.github.com/grpc/grpc-go/issues/9279">#9279</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/grpc/grpc-go/compare/v1.82.1...v1.83.2">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 修雨 <47820304+PeterGuy326@users.noreply.github.com>
…cted (#204)

## What this does

Adds `GOVERNANCE.md`, the release governance charter for this
repository: branch
protection on `main`, `refs/tags/v*` immutability, the stricter bar for
release-cut pull
requests, the CODEOWNERS policy, and an incident runbook.

It is additive. It does not change any workflow, setting, or rule, and
it explicitly
subordinates itself to live repository configuration.

## Why this is a new pull request rather than #125

#125 carries the same charter, but its head branch lives in a fork
(`waterbro-8/mem`). `.github/workflows/bytefolk-security.yml:36` guards
the `codeql` job
with:

```yaml
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
```

`codeql` needs `security-events: write` to upload SARIF, which GitHub
does not grant to a
`pull_request` run from a fork, so skipping it there is correct. The
problem is what
skipping produces. Because `codeql` is a matrix job skipped by a
job-level `if:`, GitHub
never expands the matrix and publishes the check run under the literal,
un-interpolated
name `CodeQL (${{ matrix.language }})` with conclusion `SKIPPED`. That
name matches none of
`CodeQL (go)`, `CodeQL (javascript-typescript)`, or `CodeQL (python)`,
which `main`
requires. Required contexts that are never reported are treated as
pending forever.

So #125 cannot merge at any number of approvals. `Update branch`,
auto-merge, and
re-running workflows all fail to change that. Re-pointing the work onto
a branch inside
`bytefolk/mem` is the only resolution that needs no policy change and
grants no new
permission, which is why this pull request exists.

#125 should be closed as superseded by this one.

## What differs from #125's content

The charter is otherwise the same document, but four claims in its Tag
immutability and
Incident runbook sections did not match the live configuration. They are
corrected here.

**1. "two active rulesets, neither of which has bypass actors" —
false.**

| Ruleset | Blocks | Bypass actors |
| --- | --- | --- |
| `21888356` Protect stable release tags | `update`, `deletion` | none |
| `21899500` Restrict stable release tag creation | `creation` |
repository role `admin` (`repositoryRoleDatabaseId` 5), `bypass_mode:
always` |

Read from `GET /repos/bytefolk/mem/rulesets/{id}`, and confirmed via
GraphQL
`RepositoryRulesetBypassActor.repositoryRoleName`, which returns the
string `admin`
directly rather than an id that has to be interpreted.

The asymmetry is deliberate and is now stated as such: creation stays
reachable so a
release can always be cut, while published tags are immutable for every
role including
admin. Immutability is enforced by `21888356`, not by `21899500`.

**2. "GitHub exposes no creation timestamp for rulesets" — false.**

The individual ruleset endpoint returns `created_at` and `updated_at`,
so the pair is
orderable: `21888356` at `2026-08-31T00:34:47Z`, `21899500` at
`2026-08-31T04:47:11Z`
(updated 54 seconds later at `04:48:05Z`). The charter previously
declined to say which
rule came first on the strength of that incorrect premise.

**3. "If no such path exists when a release is needed, that is a
blocker" — a path exists.**

A repository admin can cut a `v*` tag directly. `v0.1.1` demonstrates
it: annotated tag
object `c2ecc1c49ff8bbe13b9d7800bc910e4b7ac99b74` dereferences to commit
`cc727db0bc72655f299166de1f60756f5c686cc7` and is tagged
`2026-08-31T06:32:10Z`, one hour
and forty-five minutes after `21899500` became active, by a repository
admin.

**4. Incident runbook step 4 repeated claim 1.**

This is the one with operational consequences. As drafted, a maintainer
working a
compromised-release incident at 3am would have read that tag creation
"has no bypass
actors" and that a missing creation path "is escalated, not worked
around" — and would
have escalated a tag cut that a repository admin can simply perform. The
step now says it
is an admin action that requires no ruleset change, and that no ruleset
change should be
made in order to perform it.

**Probable cause of the error, now recorded in the document.** The
collection endpoint
`GET /repos/{owner}/{repo}/rulesets` renders `bypass_actors` as `null`
for every ruleset.
A reader who trusts that rendering concludes no bypass actors exist
anywhere. The
corrected section names the per-ruleset endpoint explicitly so the
mistake is harder to
repeat.

## What was verified and left alone

All nine branch-protection values the charter asserts were re-read from
`GET /repos/bytefolk/mem/branches/main/protection` and are correct as
written, so that
section is unchanged:

`enforce_admins: true` · `required_approving_review_count: 1` ·
`require_code_owner_reviews: true` · `dismiss_stale_reviews: true` ·
`require_last_push_approval: true` · `required_linear_history: true` ·
`required_conversation_resolution: true` · `allow_force_pushes: false` ·
`allow_deletions: false` · `bypass_pull_request_allowances: null`

One sentence was added to that section to keep the two controls
distinct: branch
protection having no bypass actors is a separate fact from tag creation
having an admin
bypass, and conflating them is what makes claim 1 plausible on a first
read.

The required-check enumeration in that section is unchanged and remains
explicitly hedged
as "evidence of what runs, not a substitute for the configuration". It
is dated 2026-09-03
and does not list `CodeQL (go)`, `CodeQL (javascript-typescript)`,
`CodeQL (python)`, or
`Dependency review`, all of which are in fact required on `main` today.
The hedge already
covers this, but a reviewer who wants the list refreshed should say so
rather than let it
stand as a near-miss.

## Attribution

The charter's first commit (`d2600211`, aligning it with the enforced
protections) and the
`main`-sync merges are PeterGuy326's. The commit tracking the live
CODEOWNERS policy and
both tag rulesets (`470babed`, 2026-09-03) is waterbro-8's and is
credited via
`Co-authored-by`. The four corrections above are mine and are not
attributed to waterbro-8.

## Checks

- Docs-only. Adds one file, `GOVERNANCE.md`. No code, workflow,
configuration, or
  dependency change, so no runtime behaviour is affected.
- Title is Conventional and the body links `#124` and `#125` for
`pr-policy`.
- Head branch is inside `bytefolk/mem`, so `codeql` is not skipped and
the three required
  CodeQL contexts will be reported normally.

Refs #124
Refs #125

Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
…rawal (#209)

## Summary

Resolves #207. Every MinIO image reference in this repository now
resolves from
`quay.io` instead of Docker Hub. MinIO stopped publishing container
images in
October 2025 and removed the `minio/minio` and `minio/mc` repositories
from
Docker Hub entirely, so every reference here is dead.

This is not a cosmetic dependency bump. `Validate Agent memory` →
`HTTP, CLI and MCP lifecycle` **requires** the `e2e` Compose profile,
and
`HTTP, CLI and MCP lifecycle` is a **required status context on
`main`**. So the
registry withdrawal blocked *every* pull request in this repository from
merging — not just the ones touching this stack — and no contributor
could do
anything about it.

## The failure, verbatim

```text
 minio Pulling
 minio Error pull access denied for minio/minio, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
 postgres  Interrupted
```

`postgres` reports `Interrupted` in the same step only as a consequence;
the job
dies during container startup, before any script under test is reached.

I confirmed the outage was still live before writing this PR by
re-running the
failing job on the current head of #205
([run
34857342711](https://github.com/bytefolk/mem/actions/runs/34857342711),
attempt 4), which failed at `2026-09-15T05:52:24Z` with the identical
error.

## The fix

`quay.io` still serves the same images. Before changing anything I
resolved both
digests pinned in `docker-compose.test.yml` against quay.io:

```text
quay.io/minio/minio @ sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e  -> 200 (image manifest list)
quay.io/minio/mc    @ sha256:a7fe349ef4bd8521fb8497f55c6042871b2ae640607cf99d9bede5e9bdf11727  -> 200 (image manifest list)
```

Both return their manifests, so **no image bytes change**. The
digest-pinned
test stack keeps its exact digests, the release-tagged deployment stack
keeps
its exact tags, and only the registry host differs. Same builds,
different
registry.

### Changed references

| File | Service | From | To |
| --- | --- | --- | --- |
| `docker-compose.test.yml` | `minio` |
`minio/minio:latest@sha256:14cea493…` |
`quay.io/minio/minio:latest@sha256:14cea493…` |
| `docker-compose.test.yml` | `minio-init` |
`minio/mc:latest@sha256:a7fe349e…` |
`quay.io/minio/mc:latest@sha256:a7fe349e…` |
| `docker-compose.yml` | `minio` | `minio/minio:latest` |
`quay.io/minio/minio:latest` |
| `docker-compose.yml` | `minio-init` | `minio/mc:latest` |
`quay.io/minio/mc:latest` |
| `deploy/compose/compose.yaml` | `minio` |
`minio/minio:RELEASE.2025-04-22T22-12-26Z` |
`quay.io/minio/minio:RELEASE.2025-04-22T22-12-26Z` |
| `deploy/compose/compose.yaml` | `minio-init` |
`minio/mc:RELEASE.2025-04-16T18-13-26Z` |
`quay.io/minio/mc:RELEASE.2025-04-16T18-13-26Z` |
| `deploy/compose/compose.yaml` | `minio-client` |
`minio/mc:RELEASE.2025-04-16T18-13-26Z` |
`quay.io/minio/mc:RELEASE.2025-04-16T18-13-26Z` |

### Why the non-CI files are in scope

`docker-compose.yml` is the documented local development stack and
`deploy/compose/compose.yaml` is the documented self-hosted single-node
path.
Both reference the same removed repositories.

`deploy/compose/compose.yaml` deserves specific mention: **no workflow
exercises
it**, so it would have kept a broken reference indefinitely and failed
on a cold
host for an operator following `docs/DEPLOYMENT.md`. The
`deploy/compose`
`.env.example` / `backup.sh` / `restore.sh` files were checked and carry
no image
reference of their own.

## Deliberately not changed

The `pgvector/pgvector` references. That repository is still present on
Docker
Hub, and the `PostgreSQL integration` job — which pulls
`pgvector/pgvector:pg16@sha256:00ba258a…` straight from Docker Hub on
the same
runner image at the same time — was **green in all the runs that failed
on
MinIO**. That is the evidence that this is specific to the MinIO
repositories
rather than general registry egress, and it is why the fix is scoped to
MinIO
alone. See #207 for the full run-by-run comparison.

## How this verifies itself

The failing job checks out the PR merge commit and runs
`docker compose -f docker-compose.test.yml up -d --wait postgres minio`,
so on
this PR's own head it exercises the changed file directly. `HTTP, CLI
and MCP
lifecycle` going green here is the proof, and it is also the
precondition that
unblocks the rest of the queue.

## Out of scope

- Removing the `minio/*` dependency entirely (e.g. moving to SeaweedFS)
— a
separate decision, and the Apache Flink project took that route while
Apache
  Doris took this one.
- Pinning `docker-compose.yml`'s floating `:latest` tags. Those are the
local
development stack, `scripts/validate_deploy.sh` only rejects mutable
`latest`
in the production deployment files (`deploy/`, `*/Dockerfile`), and
tightening
  them is unrelated to this outage.
## What this changes

One line deleted from `.github/workflows/bytefolk-security.yml` — the
`if:` guard on the `codeql` job. Nothing else in the file or the
repository changes.

```diff
   codeql:
     name: CodeQL (${{ matrix.language }})
-    if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
     runs-on: ubuntu-24.04
```

Blob size goes 1988 -> 1867 bytes. The deleted line is exactly 121 bytes
including its newline, so the arithmetic accounts for the whole delta:
no whitespace, reordering or annotation change rode along.

## Why

The guard's second clause compares the head repository to the base
repository. It is false for every fork pull request, so the `codeql` job
is skipped.

GitHub does not evaluate a job's `name:` expression when the job is
skipped by its `if:` guard. The skipped job publishes the raw template
string as its check name, so fork PRs in this repository report a check
literally named `CodeQL (${{ matrix.language }})`, which matches none of
the three required contexts `CodeQL (go)`, `CodeQL
(javascript-typescript)` and `CodeQL (python)`.

This is not hypothetical. Two fork PRs by `@sun-970` were measured
through `GET /repos/bytefolk/mem/commits/{sha}/check-runs` after their
first-time workflow runs were approved:

| PR | head | total | success | failed | skipped |
| --- | --- | --- | --- | --- | --- |
| #172 | `a29e9163` | 17 | 16 | 0 | 1 |
| #181 | `4a25bd25` | 17 | 16 | 0 | 1 |

The single skip in each is `CodeQL (${{ matrix.language }})`. Both are
otherwise fully green and both report `mergeable: true`, yet neither can
ever merge: the three CodeQL contexts are required and nothing the
contributor does produces them. The un-interpolated name is a symptom of
the skip, not a separate defect — on `bytefolk/digital-employee#250` the
same expression interpolated to `CodeQL (javascript-typescript)` as soon
as the job actually ran.

## Why deleting the guard rather than adding a `codeql-fork` job

`bytefolk/digital-employee#250` solved this by adding a second job with
the inverted guard. That job is identical to the baseline `codeql` job
apart from the guard and the language matrix, so a second lane
duplicates roughly 37 lines to express what deleting one line already
says.

The guard is also provably redundant for every trigger other than a fork
PR:

- `push`, `schedule`, `workflow_dispatch` — the first clause
`github.event_name != 'pull_request'` is already true.
- Same-repository `pull_request` — the second clause is already true.
- Fork `pull_request` — the job was skipped; it now runs. **This is the
only behavioural change.**

## Evidence that CodeQL actually works on a fork `pull_request` run

`bytefolk/digital-employee#250` is a fork PR (head
`PeterGuy326/digital-employee:feat/issue-245-memory-config`, sha
`abb2d58b29710927742c828a510c419c7c53efb8`). Its `codeql-fork` job
concluded `success` — check runs `102757987331` (84s) and `102757703884`
(86s).

That job declares `permissions: security-events: write` and contains
**no** `continue-on-error` on the job or on any step. A `success` job
conclusion therefore means every step succeeded, including `Analyze` —
and `github/codeql-action/analyze` fails with HTTP 403 when
`security-events: write` is absent. So SARIF upload from a fork
`pull_request` run is confirmed, not assumed.

Fork `pull_request` runs still receive no secrets and read-only
`contents`. All ByteFolk repositories are public.

## Tradeoff, stated plainly

After this change, fork PRs build untrusted code in a job holding
`security-events: write`. The run still has no secrets and read-only
`contents`, and every other required check in this repository (`Go`,
`Worker`, `Web`, `PostgreSQL integration`, `Web memory and transfer
acceptance`, `HTTP, CLI and MCP lifecycle`, `Workflow, scripts and
Compose`) already builds that same untrusted code, so this adds no new
class of exposure. It is the posture GitHub's own default CodeQL setup
takes for public repositories.

The alternatives were considered and rejected:

- `pull_request_target` would hand a write-scoped token to a workflow
run over attacker-influenced code.
- Self-reporting the three contexts through the Statuses API would
fabricate a required check that never ran.
- Granting contributors organization membership does not help at all:
the guard compares repositories, not author identity.

## What this does not touch

- No `permissions:` block changes. The job keeps exactly `contents:
read`, `actions: read`, `packages: read`, `security-events: write`.
- No required status context, branch protection rule or ruleset is
weakened or removed.
- No action SHA, trigger, matrix entry, step or job other than the
deleted line.
- The pinned `# v4.37.4` annotations are deliberately left alone. They
are the subject of bytefolk/.github#32 and #203, and this PR must stay
disjoint from them.

## Interaction with #203

#203 edits this same file, but only the three `github/codeql-action/*`
annotation lines at 58, 65 and 68, plus `bytefolk-scorecard.yml`. This
PR deletes line 36. The hunks do not overlap, so the two 3-way-merge
cleanly in either order. Neither needs rebasing because of the other.

## Checks

`Workflow, scripts and Compose` runs `actionlint` over every workflow.
Deleting a job-level conditional cannot introduce an actionlint finding;
the resulting YAML keeps `name`, `runs-on`, `timeout-minutes`,
`permissions`, `strategy` and `steps` on the `codeql` job. The release
pin validators in that job (`validate_release_action_pins.sh`,
`test_release_guards.sh`, `test_release_helpers_compat.sh`) all hardcode
`release.yml` and never read this file.

This branch is same-repository rather than a fork, so the PR's own three
CodeQL contexts can go green here — otherwise the fix could not
demonstrate itself.

## Follow-up

The identical guard sits in
`bytefolk/.github/workflow-templates/bytefolk-security.yml`, which is
where every consumer copied it from. A separate template PR is filed so
the fix propagates instead of regressing on the next template sync. Any
consumer that added its own `codeql-fork` lane (`digital-employee`, via
#250) must delete it once the template changes, otherwise two jobs
publish the same check name.

After this merges, #172 and #181 need a branch update rather than a
plain re-run: for `pull_request` events GitHub takes the workflow YAML
from the merge ref, while a re-run replays the workflow version captured
when the run was created. Both are already `state: behind` under
`strict: true`, so they need the update regardless.

Refs bytefolk/.github#35

Co-authored-by: 勒布朗-詹姆斯 <2986253039@qq.com>
## Canonical requirement

Refs bytefolk/.github#32

- Canonical Issue URL: bytefolk/.github#32
- Consumed revision: R1
- No automatic close keywords: acknowledged

Decision reference: the initial R1 Issue body. It explicitly records
that local candidates preceded this prospective publication record; no
retrospective approval is claimed.

## Requirement trace

| REQ/AC IDs | Changed files / domain | Tests or review evidence |
|---|---|---|
| REQ-001 / AC-001 | 4 exact-pinned version annotations | Exact
expected-byte replacement PASS |
| REQ-002 / AC-002 | 2 files in bytefolk/mem | Repository inventory
PASS; aggregate 7 repositories, 13 files, 21 lines |
| REQ-003 / AC-003 | Existing workflow content and modes | Parsed YAML
and comment-stripped bytes identical |
| REQ-004 / AC-004 | Current-head CI and independent review | Local
independent replay recorded in the canonical R1 Issue linked above;
hosted CI collected on head `f464f686` (19 of 20 checks succeed, see
Validation); independent human review requested and still pending |

## File domains

`.github/workflows/bytefolk-scorecard.yml` (47);
`.github/workflows/bytefolk-security.yml` (58, 65, 68).

Prepared parent / merge base: `2986fe38175f54d99f15dd38a498708c6ecd88cd`
PR base at publication: `87db0dfe0507be2190fe2fdcce0e267be8224f4d`.
Since that baseline `main` advanced by six commits through `3c13f04e`
(#162, #160, #165, #188, #158, #204) — not only `web/package-lock.json`
as previously stated here. None of them touched `.github/workflows/`, so
the F9 workflow blobs and the PR diff are unchanged. The reviewed commit
and original parent are preserved.
Head: `f464f68636adc6bb5295c3818aa6654c46a3caad` — `d2a9ec5` plus one
non-forced `Merge branch 'main'` commit (`f464f686`) that brought the
branch up to `3c13f04e` so it is no longer `BEHIND`. Verified: `git
rev-parse d2a9ec5:.github/workflows/bytefolk-scorecard.yml` and
`...:bytefolk-security.yml` return the same blobs (`2058126c`,
`48a507e0`) as at `f464f686`, and `git diff main...f464f68` is still
exactly these 2 files, `+4/-4`. The comment-only payload is therefore
byte-identical to the reviewed commit and the equality proof above holds
on the current head.

## Scope and non-goals

Correct only `# v4.37.4` to `# v4.37.9` on CodeQL uses-lines pinned to
`cdf488f595d80d6e07e03d4674febd5ab45fa938`. The [official tag
object](https://api.github.com/repos/github/codeql-action/git/tags/a35ac6e6798d72df5475948b28efb89edc2e19ca)
resolves to that existing pin. Action SHAs, permissions, triggers,
steps, matrices, other pins, and runtime code are unchanged.

## Validation

- Exact commands: `ruby evidence/verify.rb --baseline` and `ruby
evidence/verify.rb --committed` from the retained review packet; `git
diff --check 2986fe3
d2a9ec5` from this repository.
- Observed counts/results: PASS 2/2 files and 4/4 replacements here;
aggregate PASS 13/13 files and 21/21 replacements. Baseline
intentionally exits 1 after detecting all 21 stale annotations;
committed verification exits 0.
- Check URLs: collected on head `f464f686` — 19 of 20 checks succeed.
The single failure is [`HTTP, CLI and MCP
lifecycle`](https://github.com/bytefolk/mem/actions/runs/34807101354/job/103861020551),
whose log is `pull access denied for minio/minio` at ~13s: a
container-image pull failure in an unrelated job. The same workflow was
green on `main` at `3c13f04e`, and the identical failure is present on
#198 and #199, so it is not caused by this comment-only change.
Root-cause tracking is separate and open.

The strict verifier checks the changed-file allowlist; exact old blobs
and line inventory; complete expected-byte replacement; absence of stale
target annotations; parsed YAML equality; comment-stripped byte equality
and SHA-256 digests; whitespace and unchanged modes; one commit with the
exact parent; and clean worktrees with no untracked files. All passed.
The independent replay is recorded in canonical R1. The verifier and
inventory are retained outside repository commits.

| ID | REQ/AC | Observable acceptance criterion | Command or manual
steps | Environment | Expected | Observed | Status |
|---|---|---|---|---|---|---|---|
| V1 | AC-001, AC-002, AC-003 | Exact annotations with executable YAML
unchanged | `ruby evidence/verify.rb --committed` | Ruby 2.6.10, Psych
3.1.0, isolated review packet | Exact scoped replacements and equality |
2/2 files; 4/4 lines; all invariants pass | PASS |
| V2 | AC-004 | Hosted checks on this exact head | Inspect this PR's
checks at `f464f686` | GitHub Actions | Applicable checks succeed | 19
of 20 succeed; `HTTP, CLI and MCP lifecycle` fails on `pull access
denied for minio/minio` (infra, unrelated job, also failing on
#198/#199, green on `main`) | PARTIAL |

## Security and compatibility

Documentation annotation only. No dependencies, permissions,
credentials, data flows, or runtime behavior change. The diff and commit
identity were inspected for public-safe content. No CHANGELOG entry or
behavior-documentation update is needed because only explanatory
comments change.

## Known limitations

Runtime suites, build, coverage, and dependency audits were not rerun
for this comment-only change; no runtime test result is claimed. Hosted
CI is separate from local equality proof. Two limits now apply: (1) the
strict verifier's `one commit with the exact parent` invariant describes
the reviewed payload commit `d2a9ec5`, not the current branch shape,
which carries two additional `Merge branch 'main'` commits; (2) this PR
is **not merge-ready yet** — repository `AGENTS.md` step 6 requires
passing CI *and* an approval from someone other than the author, and
`HTTP, CLI and MCP lifecycle` is red on the unrelated `minio` pull, so
the green-CI half is unmet until that infrastructure failure is fixed.

## Risk and rollback

Low-risk annotation correction. Roll back through an ordinary revert of
this single commit. There is no migration or release action.

## Product review handoff

- Implementation/publication owner: @PeterGuy326
- Automated pre-review result: independent local replay recorded in R1;
no human approval implied.
- Human final review: PENDING; no human review requested by this
publication.
- Merge ledger owner: @PeterGuy326
- Product reviewer: @PeterGuy326
- Milestone or release packet: N/A: bounded documentation annotation
maintenance
- Merge, CI, release, and model judgment do not accept or close the
Issue: acknowledged


## Maintenance update (2026-09-14, @waterbro-8)

Records written by the maintainer account, not by the implementation
owner:

- `f464f686 Merge branch 'main'` was pushed to this head branch
(non-forced, `main` at `3c13f04e` is an ancestor of the head) to clear
the `BEHIND` state this PR's own body said blocked merging. No workflow
file content changed: both blobs are identical to `d2a9ec5`.
- The stale facts above were corrected in place: the recorded head SHA,
the "Main advanced only `web/package-lock.json` in PR #192" claim, the
`NOT VERIFIED` hosted-CI rows, and the "this is a draft, not
merge-ready" note.
- This PR was marked ready for review and an independent review was
requested. The maintainer account that pushed the merge commit did
**not** approve it: `AGENTS.md` step 6 requires an approval from someone
other than the author, and a commit author on the head cannot supply
that approval for their own push. `@PeterGuy326` remains implementation
and merge-ledger owner.

Co-authored-by: 勒布朗-詹姆斯 <2986253039@qq.com>
…ies (#181)

## Summary

Fixes #179.

`GOAL.md` §5 and §6 contain four status statements that list
capabilities as missing when they are already merged and reachable on
`main`. This PR corrects them to match what the code actually supports.

## Changes

### §5 — 现状与愿景的偏差 table

- **跨设备恢复**: Separate `merge_conservative` (done, `merge.go` +
`0023_workspace_import_merge.sql`) from 增量同步 and 断点上传 (not done). The
old wording made all three look missing when only two are.
- **人类可视化**: correction/supersede relations are merged
(`0022_memory_relations.sql`, `relation.go`); only audit history remains
open.
- **数据可移植性**: `merge_conservative` import is merged; update "当前服务只支持
fresh restore" to reflect that merge is now supported.

### §6 — 近期优先级

- **P1**: Split `merge_conservative` (checked) from 增量包/断点上传 (still
open). Previously one unchecked item bundled done and undone work
together.
- **P2**: Check off correction/supersede, 导入历史 and 权限管理界面 — all shipped
via PRs #90/#95, #102, #100.

## Evidence

Each correction is backed by source-level evidence (file presence +
migration content) as documented in #179. No runtime verification was
performed; see issue's "Evidence level: E2 — source-level" note.

## Test plan

- [ ] Review each corrected line against the cited source files.
- [ ] Confirm §5 rows now separate done from not-done within each
capability.
- [ ] Confirm §6 checkboxes match the merged PR list.

Co-authored-by: 勒布朗-詹姆斯 <2986253039@qq.com>
## Problem and result

Refs #211 (revision r2) and bytefolk/design-system#29.

The Web client had faint light-theme labels, inconsistent control colors
and broadly centered reading content. It now derives its palette from
the shared design-system source and aligns content by purpose:
names/forms/headings start-align, comparable numeric columns and
trailing actions end-align, and button contents and complete empty
states center. Narrow layouts keep controls reachable.

## Changes

- Add a zero-dependency token generator using an unmodified, licensed
snapshot of design-system commit
`910456901dda74da4d5b0320cd03d36ad18650b0`; verify its SHA-256 and
generated output before every Web build. Preserve alpha and derive
readable foreground/solid-action pairs from those source hues.
- Keep React 19 and existing primitives; add no runtime dependency or
peer-range change. A local empty-state adapter follows the shared
title/description/action scale.
- Restore reading alignment across navigation, files, filters, forms,
cards and dialogs; keep localized confirmation labels visible when
omitted by callers.
- Use a device-width viewport, wrap provider/detail actions, and keep
permission tables locally scrollable on phones. Theme browser chrome
follows computed tokens.
- Update Unreleased changelog. API, routes, storage and authorization
behavior are unchanged.

## Validation

With locked dependencies (`cd web && npm ci`), `make test-web` passed:
type/lint/build and the existing localization, theme, enrichment,
memory, managed embedding and transfer acceptance. Unit tests: 7 files /
69 passed; root independently reran the 2 confirmation regressions.

The token check verifies 334 generated/composited pairs at >=4.5:1.
Actual browser acceptance captured 82 states across both themes at
desktop and 390px touch-mobile widths, covering populated/empty views,
dialogs, menus, file/memory/task details, providers, permissions and
transfer. All 4,758 sampled visible-text pairs met the selected contrast
threshold, with no JavaScript errors or unexpected document overflow.
The mobile permission table was actually swiped to its trailing action
and the confirmation opened. Root independently inspected the running
Web client. A separate source/render review caught and verified the
failed-thumbnail badge fix (9.23:1 light /10.14:1 dark). A further12
primary/danger/disabled normal+hover samples reached at least5.55:1;
true touch scrolling also kept file-list names and numeric columns
reachable. This is fixture-based UI acceptance: all API responses came
from MSW; it is not a live-backend integration result. Private
screenshots remain local.

## CI dependency and review status

Keep this PR **Draft** until required current-head CI passes. The
preceding head's HTTP/CLI/MCP lifecycle check failed before application
tests because the pinned MinIO image could not be pulled from Docker
Hub. The separate fix is #209 (issue #207); it is not copied into this
UI diff. Other green checks do not waive that dependency. Current-head
results must be read independently after this push.

No deployment or formal human approval is included. Revert the scoped
commits to roll back; no migration is required.

## Current-head hosted result

All checks have finished on `7d941998167c4c6dadd27be348d9b96e1ec28e03`.
The only failing check is [HTTP, CLI and MCP
lifecycle](https://github.com/bytefolk/mem/actions/runs/35047528915/job/104640536818);
all other check entries succeeded, including Web and Web memory/transfer
acceptance. The failing job stops while starting isolated dependencies:
`minio/minio` reports pull access denied before the application tests.
This matches the separate registry fix in #209. The PR remains Draft
pending that dependency and a fresh successful CI run.

Totoro received the review bundle and explicit blocker status, with
successful message delivery verified. This is a handoff, not formal
approval.
…cessor to #170) (#199)

## Tracking record and provenance

Refs #122 — bounded release-validation readiness, not publication or
completion
of the release issue. Canonical draft successor to #170. Original author
`sun-970` and maintainer commits are preserved in ancestry. #170 remains
open
with its original discussion and reviews; no original review is
transferred
as an approval of this candidate.

- Original/final source head:
`fa2c30cc693bc0ae1e679e7d108d59aef1bd4007`.
- Successor head: `260710d69a66f919520e5a4dccc3749335eb95fc`.
- Integration commit `896aedb0069f688db3c46702147cf9ea7e015ba9` has
normal
merge parents, in order: source
`fa2c30cc693bc0ae1e679e7d108d59aef1bd4007`
  and actual canonical main `2986fe38175f54d99f15dd38a498708c6ecd88cd`.
- Final successor tree: `5f1313f778ae2dc964c20d350b8829a365852100`.
- The integration merge was conflict-free. Its only tree additions over
source were main's
  unchanged `.github/workflows/bytefolk-security.yml` and
`.github/workflows/bytefolk-scorecard.yml`; source files remained exact
at
that integration commit. The subsequent `260710d` adds only the
separately
  authorized output-path safety fix and regressions described below.
- The lockfile retains #192 source
`11e02e21ef2c3dbd2dae26e4376872e54e78ecb5`
with its `cherry-pick -x` traceability. No original commit was
rewritten.

## Summary and acceptance criteria

CHANGELOG comparison links must use the exact preceding version and
release
endpoint. Missing predecessors fail closed; exact release-tag links
remain
valid. GNU/Linux checksum enumeration must process each basename
separately,
accept the correct six assets in a directory with spaces, reject an
empty set
with an explicit diagnostic, and emit no manifest for invalid input.

An independent preflight then reproduced a separate unchanged-baseline
gap:
an existing `mem-mcp-checksums.txt` symlink to a directory made `mv`
publish
the temporary manifest outside the asset directory. A later checksum
failure
did not undo that write. This was NOT introduced by the basename fix and
was
NOT fixed at original head `fa2c30cc`. This successor now rejects all
existing
manifest paths (including dangling symlinks) before hashing and
immediately
before publication, preserving existing paths and external data. Staging
still
uses randomized `mktemp`, not a predictable filename.

The canonical successor resolves the separate CI execution-path gate:
main
requires three CodeQL language contexts, but the existing workflow skips
fork
PRs. This same-repository candidate uses the existing allowed path
without
editing security permissions, fork restrictions, required checks or
tests.

Changed files against main: `CHANGELOG.md`,
`scripts/generate_release_checksums.sh`,
`scripts/test_release_guards.sh`,
`scripts/validate_release_version.sh`,
`scripts/test_release_checksum_output_safety.sh`,
`web/package-lock.json`. No security
workflow differs from main.

## Reproduction and attribution

On real GNU/Linux, base `7a194f1e` passes the release guards, while
pre-follow-up
`1624cf74` fails with GNU basename's `extra operand` error because `find
-exec
basename {} +` batches paths. The fix invokes basename once per path
with
`\;`. The new comparison regression fails against the base wildcard
validator
with `wrong compare base v0.0.0: command unexpectedly succeeded`.

The earlier macOS `find -printf` failure was not caused by the original
comparison-link patch: its checksum script was byte-identical to base.
[Public correction and
evidence](#170 (comment)).
Do not confuse the Bash compatibility suite's find stub with actual GNU
semantics; the full guard suite uses real GNU find/basename/sha256sum.

Output-safety negative controls: the new focused regression fails on
both
canonical main `2986fe38` and pre-safety integration head `896aedb0`
with
`symlink-directory: output publication changed existing data or created
an unexpected file`.
The fixed tree passes seven cases with path spaces:
symlink-to-directory,
symlink-to-file, dangling symlink, existing directory, existing regular
file,
an output symlink introduced during hashing, and a pre-existing
template-shaped
temporary symlink. File-content snapshots verify external data and
existing
outputs are preserved; the late-symlink case also verifies private
staging
cleanup. This is bounded path-safety validation, not a claim of atomic
defense
against a hostile process concurrently replacing paths after the final
check;
release staging must remain controlled by the release job.

## Validation ledger

Environment: isolated Linux aarch64, Linux 6.8, GNU find 4.9.0/coreutils
9.4,
Bash 5.2.21, Node 24.13.0/npm 11.6.2. The Node download is
checksum-verified.
Fresh exact Git bundle clone, two CPUs, GOMAXPROCS=2 and GOFLAGS=-p=1.

| Command/check | Expected | Actual |
| --- | --- | --- |
| `git merge-base --is-ancestor fa2c30c
HEAD` | Source ancestry retained | PASS |
| Diff source..integration commit excluding the two unchanged main
workflow additions | Source files exact before new safety fix | PASS,
empty diff |
| `git diff --stat 896aedb HEAD` | New
baseline safety fix only | PASS, four scoped files: generator, focused
tests, full-suite hook, changelog |
| Diff main..HEAD for all security/release/publish/policy/acceptance
workflows | No workflow changes | PASS, empty diff |
| Root: `bash scripts/test_release_guards.sh` | Comparison and
six-asset/empty-set guards fail closed | PASS, new comparison-link and
full guard PASS |
| `bash scripts/test_release_checksum_output_safety.sh` | Existing
output paths fail without external writes; random staging remains safe |
PASS, seven focused cases on final tree |
| `bash scripts/test_release_helpers_compat.sh` | Collection
compatibility | PASS |
| `bash scripts/test_validate_release_action_pins_compat.sh` | Official
action pin compatibility | PASS |
| `bash scripts/validate_release_version.sh 0.1.1` | Version surfaces
agree | PASS |
| `shellcheck scripts/generate_release_checksums.sh
scripts/test_release_checksum_output_safety.sh
scripts/test_release_guards.sh scripts/validate_release_version.sh` | No
shell diagnostics | PASS, ShellCheck 0.11.0 on macOS |
| From `web/`: `npm ci --registry=https://registry.npmjs.org
--fetch-timeout=45000` | Locked install | PASS, 401 installed packages |
| `npm run audit --registry=https://registry.npmjs.org` | Both unchanged
thresholds pass | PASS, zero vulnerabilities |
| `npm test` | Application regressions pass | PASS, 67 tests / 6 files |
| `npm run lint` / `npm run typecheck` / `npm run build` | Static
validation and build pass | PASS, existing large-chunk warning only |
| `git diff --check 2986fe3 HEAD` |
Clean diff | PASS |
| Canonical successor CI and CodeQL | All required named contexts
execute and pass | PENDING at draft creation; no all-green claim |

## Tests, coverage and known limits

The source-head
[CI](https://github.com/bytefolk/mem/actions/runs/34437665487)
and
[acceptance](https://github.com/bytefolk/mem/actions/runs/34437665500)
passed; CodeQL was skipped there and did not satisfy its required
contexts.
Those runs are source evidence, not the canonical successor's CI or
review.
Coverage percentage was not remeasured. The new output-path safety
regression
runs through the existing release-guard CI command; no job or check is
removed.

Full stock-macOS release-guard portability is not claimed: existing BSD
`wc -l` padding affects a row-count assertion. The full suite is
validated
with real GNU tools. The existing large-chunk build warning remains.
Windows,
browser, Go/process and database acceptance use unchanged CI. No
temporary
macOS Go executable is run; fixtures are disposable and create no real
release.

## Risk, rollback and review

Build/release-validation/dependency maintenance, no API/CLI/MCP
authorization,
storage or migration contract change. `[Unreleased]` documents the
behavior.
No custom repository secret is referenced by PR workflows; CodeQL uses
its
existing security-events upload permission. Release/npm publication and
Scorecard are not triggered by these candidate branches. No tag,
release,
original-PR closure, main merge, protection change or formal approval
occurs.
Rollback is to leave this draft unmerged; no deployed behavior changes.

Draft pending exact-head required CI and independent review. The Owner
relayed
an independent preflight PASS for the original comparison/enumeration
delta;
that preflight found the baseline output-symlink gap and does not cover
this
new safety commit. Fresh review of `260710d` remains required. Original
human-authored commits are retained; automated
assistance helped with maintainer remediation and validation evidence.

---------

Co-authored-by: liyuanyang <liyuanyang@users.noreply.github.com>
Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
Co-authored-by: 勒布朗-詹姆斯 <2986253039@qq.com>
#198)

## Tracking record and provenance

Refs #122 — bounded CI/release-readiness follow-up, not completion or
publication
of that release. Canonical draft successor to #169, with the original
author's
`sun-970` commits and maintainer follow-ups retained unchanged in
ancestry.
The original PR stays open; its reviews and discussion remain
authoritative
history, not an approval of this successor.

- Original/final source head:
`5c3a4a75ab96bed4c046c0822ebc76a87f215cda`.
- Successor head: `5c3a4a75ab96bed4c046c0822ebc76a87f215cda`.
- Source and successor tree: `9d8a127375fd8e945113e06ae88983a1328b9fae`.
- Current canonical main: `2986fe38175f54d99f15dd38a498708c6ecd88cd`,
already
an ancestor. A normal merge reports `Already up to date`; no empty
commit,
  cherry-pick reconstruction, rebasing, or force push was used.
- The audited lockfile retains #192's provenance from
`11e02e21ef2c3dbd2dae26e4376872e54e78ecb5` (`cherry-pick -x` in
ancestry).

## Summary and acceptance criteria

The Web CI job must run its unit tests. Recognized transient npm audit
failures
may retry, but vulnerabilities, unknown errors, failed spawns, signals
and
timeouts must remain failures. Successful audit output must remain
visible,
including below-threshold findings. The Windows evidence helper must
complete
its report and preserve a nonzero audit exit status.

This successor changes no source file relative to #169's exact source
head.
It exists because the canonical security workflow skips the entire
CodeQL job
on fork PRs while main requires three language-specific CodeQL contexts.
A
same-repository candidate executes the existing supported path; no
security
workflow, permission, required check, audit threshold, or fork
restriction is
modified or waived.

Changed files against main: `.github/workflows/ci.yml`, `CHANGELOG.md`,
`scripts/test_win_audit_verify.mjs`, `scripts/win-audit-verify.bat`,
`web/audit-retry.mjs`, `web/audit-retry.test.mjs`, `web/package.json`,
`web/package-lock.json`, `web/vite.config.ts`.

## Validation ledger

Environment: isolated Linux aarch64, Linux 6.8, GNU find 4.9.0/coreutils
9.4,
Bash 5.2.21, Node 24.13.0/npm 11.6.2; checksum-verified official Node
archive.
Fresh Git clone from an exact local bundle, two CPUs, GOMAXPROCS=2 and
GOFLAGS=-p=1. Tests use disposable fixtures, not production data or
secrets.

| Command/check | Expected | Actual |
| --- | --- | --- |
| `git diff --exit-code 5c3a4a7 HEAD` |
Exact source tree | PASS, no differences |
| `git merge-base --is-ancestor 2986fe3
HEAD` | Current main included | PASS |
| From root: `bash scripts/test_release_guards.sh` | Release guards fail
closed | PASS |
| `bash scripts/test_release_helpers_compat.sh` | Collection
compatibility | PASS |
| `bash scripts/test_validate_release_action_pins_compat.sh` | Official
pinned actions and compatibility | PASS |
| `bash scripts/validate_release_version.sh 0.1.1` | Version surfaces
agree | PASS |
| From `web/`: `npm ci --registry=https://registry.npmjs.org
--fetch-timeout=45000` | Locked install | PASS, 401 installed packages |
| `npm run audit --registry=https://registry.npmjs.org` | Both unchanged
thresholds pass | PASS, zero vulnerabilities, both reports printed |
| `npm test` | Application and audit regressions pass | PASS, 104 tests
/ 7 files, including 37 audit regressions |
| `npm run lint` / `npm run typecheck` / `npm run build` | Static
validation and production build pass | PASS, existing large-chunk
warning only |
| `git -c
core.whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol
diff --check 2986fe3 HEAD` | Clean
diff, intentional batch CRLF retained | PASS |
| Canonical successor CI and CodeQL | All required named contexts
execute and pass | PENDING at draft creation; no all-green claim |

## Tests, coverage and known limits

The original successful-report regression was red before the fix (1
failed /
36 passed). Existing exact-source [Web
CI](https://github.com/bytefolk/mem/actions/runs/34438230117/job/102747885693)
passed 104 tests including 37 audit regressions. Existing real
[Windows
CI](https://github.com/bytefolk/mem/actions/runs/34438230117/job/102747885554)
passed four native batch-helper regressions, including negative controls
for
both original defects. These are source-head evidence, not independent
review
or substitute results for the new canonical workflow execution.

Coverage percentage was not remeasured. A real-registry Windows audit
has not
been requalified; its helper tests use local npm.cmd fixtures. The
unchanged
five-second process fixture timeout reproduces on both pre-follow-up and
fixed
heads on the managed macOS host; Linux is the validated environment. The
existing large-chunk build warning remains. Full Linux process/database
and
browser acceptance, and native Windows execution, are delegated to
unchanged
CI; no temporary Go executable runs on macOS.

## Risk, rollback and review

Build/CI/dependency maintenance; no application API, CLI/MCP
authorization,
storage or migration contract changes. `[Unreleased]` already documents
the
behavior. The PR-triggered workflows reference no custom repository
secrets;
CodeQL retains only its existing security-events upload permission. No
release,
npm publication, Scorecard dispatch, tag, merge, or formal approval is
performed.
Rollback is to leave this draft unmerged; no deployed behavior changes.

Draft until the exact candidate has required CI plus independent review.
The
Owner reports independent review of the fix deltas is already in
progress;
this description does not represent that review as completed. Automated
assistance produced validation and the maintainer follow-up; the
original
human-authored history remains intact.

---------

Co-authored-by: liyuanyang <liyuanyang@users.noreply.github.com>
Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
)

## Linked draft successor — original #183 remains open

Refs #176.

## Current follow-up: `c0421168bd145077bf164f91c0d2b454a79760ef`

- Fixed the remaining exported MCP route enum: `tools/list` now includes
`lexical`. The in-process MCP regression reproduced the missing enum
before the fix and now verifies both schema and `tools/call` HTTP
forwarding. All 9 MCP tests pass on Linux; affected-package vet passes.
- Added a strict, populated PostgreSQL sequential-upgrade regression and
a DB-free embedded-migration continuity guard. Actual PostgreSQL 16.14 /
pgvector 0.8.2 advances 23 → 24, verifies lexical backfill and full
history, and accepts ordinary production startup afterward. No
`AllowMissing` option or migration renumbering.
- Cumulative source/base order is #194 → #197 → #195. #197 remains HOLD
for text-query planner acceptance; #195 remains HOLD behind it. See
`docs/MIGRATION_SEQUENCE.md` for deployment and existing-gap recovery
boundaries.
- Tests ran at `2f2e965b6794863d7f5a38a748262da4691beb35`; this final
head only corrects the documented verification command to
`scripts/verify.sh integration`. Fresh exact-head CI and independent
review remain required. The earlier evidence below is historical, not a
fresh-head approval.

Preserves @sun-970 / liyuanyang's complete authored chain through
`13ebe9efa6ba3c733199d374e8db3d107f598ca2` for #176. This draft makes
the bounded reviewed corrections accessible; it does not replace
independent review, CI, or human approval. Do not close #183 before a
replacement is verified and merged.

The original fork's Git-data write returned HTTP 404 and its repository
permissions report `push:false`. No ACL override was attempted. This
canonical branch preserves original commit identities and hashes. Added
blobs, trees, and commits were checked against local Git hashes; no
force update was used.

## Corrections

- `scripts/verify.sh`: expect migration 24, matching this branch's
lexical migration. The former expectation of 23 caused the PostgreSQL
validation failure.
- Refresh audited Web development dependencies, traceable to #192 commit
`11e02e21ef2c3dbd2dae26e4376872e54e78ecb5` via a separate `cherry-pick
-x` commit. Audit threshold unchanged.
- No additional search/ranking changes beyond original #183.

## Validation at head `3d885a8427e06fd7175011ef0188a06f32028a3f`

- PASS: migration to 24 on PostgreSQL 16.14 / pgvector 0.8.2 and
PostgreSQL 17.10 / pgvector 0.8.3.
- PASS: `TestLexicalSearchWithoutWorker`, five cases: lexical without
worker, text/auto fail closed, CJK trigram, path restriction. Go tests
cross-compiled for Linux and executed against real disposable PostgreSQL
with `GOMAXPROCS=2` and serial package builds.
- PASS: audited lockfile check; shared fix from #192 remains explicit,
not attributed to this feature.
- NOT CLAIMED: provider-backed vector retrieval or production benchmark
quality.
- REQUIRED: fresh CI on this exact head and independent review.
Original-head CI/review is not approval of this draft.

Original feature scope: model-free file-corpus lexical route, migration
0024, API/CLI/docs, managed-provider bypass. Scope excludes tokenizer,
generation lifecycle, and ranking redesign.
) (#196)

## Linked draft successor — original #184 remains open

Refs #175.

Preserves @sun-970 / liyuanyang's authored chain through
`89bb12bdf2ae5edbdfa17260953ec6352ab9239f` for #175. Do not close #184
before a replacement is independently verified and merged. This draft
remains HOLD for live acceptance.

Source-fork Git-data writes returned HTTP 404, with repository
`push:false`; no ACL override was attempted. The canonical branch
preserves original commit hashes and identities. Added blobs, trees, and
commits were individually hash-verified; no force update was used.

## Bounded corrections

- Fail closed on ambiguous cross-workspace mappings, unknown result
paths, malformed responses, non-finite scores, and failed requests;
retain an error artifact and exit 2.
- Map the shipping folder `path` plus file `name`; do not silently
discard unknown hits or infer tenant identity from snippets.
- Vector mode sends `route=text`; lexical mode sends `route=lexical`
with null provider/model/dimension metadata. Do not claim `auto` is
lexical/vector hybrid.
- Reject structured-memory queries this file-search endpoint cannot
serve. Document the existing file-only `profile-text-v1` fixture as the
bounded corpus.
- Remove hostname collection and invented provider/index identity.
Configuration labels remain explicitly operator-declared, not
server-verified.
- Carry #192 audit remediation as a separate `cherry-pick -x` of
`11e02e21ef2c3dbd2dae26e4376872e54e78ecb5`; audit threshold unchanged.

## Validation at `651bec1679c50a9cd07cf77b27b5556c20e3273e`

- PASS: Python 3.11.14, `python3.11 -m unittest discover -s
benchmarks/recall/tests`: 39 tests, including the reproduced fail-closed
regressions and a loopback HTTP fixture.
- PASS: `python3.11 -m benchmarks.recall verify`: deterministic harness
and intentional leakage failure gate.
- PASS: Web audit with the explicit shared fix.
- NOT VERIFIED: real memd retrieval, actual embedding-provider quality,
real index selection, production latency, or full structured-memory
corpus acceptance. The HTTP handler is a fixture, not memd; its timing
is not live benchmark evidence.

## Exact remaining live prerequisites (no external provider authorized)

1. An isolated, authorized test deployment of real memd and its Worker,
with PostgreSQL/pgvector and ingest dependencies configured, plus a
token verified to belong to the test workspace. No existing user
deployment or provider credentials have been used.
2. Ingest all five synthetic files from
`benchmarks/recall/data/profile-text-v1/corpus.jsonl`, preserving their
full paths and contents, into that workspace. The producer is not an
ingestor. Confirm indexing completed and file/result identities match
the fixture.
3. For the bounded fixed-text experiment, use the same locally
available, explicitly selected 768-dimensional text embedding model for
corpus and query. Verify the corpus/provider metadata and which active
generation or fixed table the server actually uses. A label passed to
the producer proves none of these facts. No paid provider, external
endpoint, model download, or provider configuration was enabled by this
correction.
4. Execute all four file queries through real `/v1/search` with `--mode
vector`, retain sanitized rankings, then score with `run --rankings` and
record the exact memd head, actual model/dimension/index, environment,
and errors. An empty/error run does not satisfy live acceptance.
5. Model-free lexical is a separate optional real-server experiment
requiring #183's server capability (draft successor #194); it cannot
establish vector quality. Full v1 structured-memory acceptance remains
unsupported by this producer and must not be reported as passed.

Fresh exact-head CI and independent review/human approval remain
separate required gates. No fake or paid live run is substituted for the
missing evidence.
Refs #111

## Requirement and scope

Re-lands #147 onto current `main` as an organization branch. #129/#147
were closed under the 2026-09-03 fork-workflow decision, not as a
judgment that the extraction was wrong. Blocker PR #108 is already
merged.

Preserves qoder behaviour: same memories payload shape, same
`Idempotency-Key` derivation for a canonical absolute root, same stdout
summary, same cursor file format/location. Adds the OS-backed cursor
lock from the #147 follow-up so concurrent writers do not share a `.tmp`
name.

## Changes

- New `server/internal/ingest` package: walk, per-path cursor (atomic
rename, shrink-reset), `--dry-run`/`--limit`, closed failure codes,
report aggregation.
- `mem ingest qoder` is a thin connector (parser + HTTP upload).
- OS advisory lock around cursor load/save (`cursor_lock_*.go`).
- No `fsnotify`, no `--watch` (#110 stays a successor).

## Validation ledger

| ID | Criterion | Command | Status |
| --- | --- | --- | --- |
| V1 | Qoder tests with import-path changes | `go test ./cmd/mem -run
Ingest` | NOT VERIFIED locally — host Go 1.22, module requires 1.25 |
| V2 | Core fixtures: dry-run, shrink-reset, 409 degrade, corrupt cursor
| `go test ./internal/ingest` | NOT VERIFIED locally — same toolchain
gap |
| V3 | `git diff --check` | local | PASS |
| V4 | No cobra/stdout in the core package | source review of
`server/internal/ingest` | PASS |

Independent review still required. No merge or issue close.

Original extraction: @waterbro-8. Cursor lock follow-up: @sun-970 /
liyuanyang. Canonical-path identity follow-up: 勒布朗-詹姆斯.

---------

Co-authored-by: waterbro-8 <waterbro-8@users.noreply.github.com>
Co-authored-by: liyuanyang <liyuanyang@users.noreply.github.com>
Co-authored-by: 修雨 <47820304+PeterGuy326@users.noreply.github.com>
## Summary

Completes the work [#213](#213) left
undone against [#173](#173).

- Migration **0025** (0024 is already lexical from #194) adds cosine
HNSW indexes on `embeddings_text` (768), `embeddings_visual` (512), and
`embeddings_face` (512) with `vector_cosine_ops`.
- Shipping **text** search no longer uses `DISTINCT ON (f.id) ORDER BY
f.id, distance` as the primary plan. That shape cannot use HNSW. It now
walks `ORDER BY embedding <=> $1 LIMIT n`, keeps the first sighting of
each file, excludes selected files, and **falls back** to the original
exact `DISTINCT ON` query if a bounded scan underfills (one file owning
many near chunks).
- Relator text neighbors use the same continuation/fallback.
- Visual already matched HNSW. Face clustering stays in-process; the
face index is DDL only.
- `index_generation_vectors` is not indexed (scope boundary).
- Transactional `CREATE INDEX` (not `CONCURRENTLY`): a failed concurrent
build leaves an `INVALID` index that `IF NOT EXISTS` will skip.
- Wrong dimensions fail at the `vector(N)` column. Recall is **not**
claimed; harness is [#175](#175).

Does not reopen #213. #197 remains the earlier HOLD attempt; this branch
is based on current `main` (schema 24) and takes 0025.

## Changes

- `server/internal/db/migrations/0025_ann_hnsw_indexes.sql`
- `server/internal/search/search.go` — text continuation + exact
fallback
- `server/internal/relator/relator.go` — same policy
- `TestHNSWMigrationPostgres` — populated 24→25→24→25, ingest, dimension
rejection, EXPLAIN
- `TestTextANNFileSemanticsPostgres` — 101-chunk file still returns 10
eligible files
- `scripts/verify_hnsw_indexes.sh` + `scripts/verify.sh` head 25
- `docs/VALIDATION_HNSW.md`, SPEC, CHANGELOG

## Validation ledger

| ID | Criterion | Command | Status |
| --- | --- | --- | --- |
| V1 | Server builds, unit contracts | `make test-server` | Pending CI
(sandbox has Go 1.22; module requires 1.25) |
| V2 | Worker | `make test-worker` | Not affected |
| V3 | Web | `make test-web` | Not affected |
| V4 | Race | `make test-race` | Pending CI |
| V5 | Fresh schema, rollback, PostgreSQL | `make test-integration` |
Pending CI — `EXPECTED_MIGRATION_HEAD=25`; `TestHNSWMigrationPostgres` +
`verify_hnsw_indexes.sh` |
| V6 | DB race | `make test-integration-race` | Pending CI |
| V7–V9 | Acceptance / MCP / visual quality | — | Not affected |
| V10 | Recall | `make test-recall` | Not measured. Recorded harness:
#175 |
| Text semantics | 101-chunk file still yields k files |
`TestTextANNFileSemanticsPostgres` | Pending CI |
| Planner | EXPLAIN uses HNSW for text cosine-order and visual |
`TestHNSWMigrationPostgres` + `scripts/verify_hnsw_indexes.sh` | Pending
CI. No `enable_seqscan=off`. |

Local sandbox could not run Docker or download Go 1.25, so EXPLAIN was
not executed here. CI `memory-validation.yml` / `verify.sh integration`
is the evidence path.

## Design notes

- Defaults `m=16`, `ef_construction=64`. No iterative-scan GUC.
- Empty `uuid[]` exclude lists are never sent as SQL NULL (`ANY(NULL)`
would drop all rows).
- Editing 0001/0019 is comment-only. Goose does not checksum like
Flyway.

Fixes #173

---------

Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
…) (#216)

## Requirement and scope

Re-lands #157 onto current `main` as an organization branch. #157 was
closed because the fork head could not carry CI, not because the work
was rejected.

Refs #151. Implementation owner on the issue is @waterbro-8; human
review owner is @Bindy-lbb. Original implementation: @sun-970.

## Changes

- Release workflow builds `memd`, `mem-migrate`, `mem-healthcheck`, and
`mem` for linux/darwin amd64/arm64 in addition to `mem-mcp`.
- Two checksum manifests: `mem-mcp-checksums.txt` (6) and
`mem-checksums.txt` (16). The post-#199 output-path protections (no
process-substitution find, no `wc -l`, refuse existing/late output
symlinks, private mktemp staging) are kept and applied to both
manifests.
- `/v1/version` exposes distinct `version` / `revision` / `contract`
fields; CLI `mem version` prints them and still redacts the server URL.
- Docker image and DEPLOYMENT.md first-run notes from #157.

## Validation ledger

| ID | Criterion | Command | Status |
| --- | --- | --- | --- |
| V1 | Checksum output safety | `bash
scripts/test_release_checksum_output_safety.sh` | PASS |
| V2 | Release guards | `bash scripts/test_release_guards.sh` | PASS |
| V3 | Bash 3.2 helper compat | `bash
scripts/test_release_helpers_compat.sh` | PASS |
| V4 | `git diff --check` | local | PASS |
| V5 | Go API/CLI tests | `make test-server` | NOT VERIFIED — host Go is
1.22.12, module requires 1.25.0 |
| V6 | Built release binaries on six platforms | release workflow | NOT
VERIFIED — no tag cut |
| V7 | Client revision pin against a published memd | needs published
artifact | NOT VERIFIED |

No tag, GitHub Release, npm publish, merge, or issue closure is
performed by this PR.
…221)

## Summary

Lands the **contract-only** MEM-1 envelope for
[#220](#220). Pins RoleWeave
[#327](bytefolk/roleweave#327) R1 and the P1
correction on
[roleweave#345](bytefolk/roleweave#345):
`durable-memory.v1` cannot ship `scope` as a free string.

Isolation is **workspace + position principal + `memory_scope` +
grant/revocation**. Grant rows are reused from `durable-context.v1`;
`capability-grant.v1` is a normative pointer (`server=mem`).
`grant.mode` is `read` only. Forget uses the mem `delete` token scope
plus a workspace role that allows deletion.

**No HTTP, SQL migration, or MCP wiring.** Runtime waits for Gate D0.
Live E3 evidence is still required before a later PR may claim
production recall. This PR does not close #220.

## Changes

- `docs/schemas/durable-memory.v1.schema.json` + example
- `docs/DURABLE_MEMORY.md` — how grant/revocation enter readback/receipt
- `docs/adr/0006-durable-memory-v1.md`
- `server/internal/durablememory` — decode (unknown fields / free
`scope` fail closed), eligibility, exact readback, permissioned forget,
receipt projection
- CHANGELOG `[Unreleased]`

## Acceptance

| AC | How |
| --- | --- |
| AC-001 | Required `binding` + `grant` (`grant_id`, `grant_version`,
`permission_digest`, `revoked_at`). Schema has no `scope` property. |
| AC-002 | Evaluator omits expired / revoked / malformed / superseded /
forgotten / out-of-scope. |
| AC-003 | `ExactReadback` compares canonical envelope. Pin may preserve
TTL eligibility; pin does not restore a revoked grant or another
principal. |
| AC-004 | `EvaluateForget` requires `delete` token scope + workspace
delete role. Failure is `forget_denied`. `Deleted` stays false; never a
local fake delete. |
| AC-005 | `PhysicalDeleteImplied` is always false. TTL is recall
eligibility only. |
| AC-006 | Pins #327 R1. No runtime surface. |

## Validation ledger

| ID | Criterion | Command | Status |
| --- | --- | --- | --- |
| V1 | durable-memory contract tests | `go test
./internal/durablememory/ -count=1` (from `server/`) | PASS (0.010s) |
| V2 | Worker | — | N/A, no worker change |
| V3 | Web | — | N/A, no web change |
| V4 | Race | `make test-race` | NOT VERIFIED — CI |
| V5 | PostgreSQL / migrations | — | N/A, no migration |
| V6–V9 | Acceptance / MCP / visual | — | N/A, contract only |
| V10 | Live E3 recall | — | NOT VERIFIED. Required before runtime.
Fixtures do not substitute. |

## Design notes

- `binding.workspace_id` is the **mem** workspace. `memory_scope` is
`/workspaces/<digital-employee instance>/positions/<position_id>`. Those
UUIDs are different namespaces.
- `permission_digest` is SHA-256 of the canonical `(workspace_id,
principal, memory_scope, mode, grant_version)` tuple.
- Out-of-scope / malformed probes return an empty receipt (no memory id,
locator, or grant block). In-scope denials keep grant status so
operators can see why recall stopped.
- Recalled text is `trust=untrusted`, `authority=none`.

Refs #220

---------

Co-authored-by: asteam-worker <asteam-worker@users.noreply.github.com>
…est (#191)

Refs #139.

#217 already extracted the qoder checkpoint lock into
`server/internal/ingest`. This PR no longer reintroduces
`server/cmd/mem/qoder_checkpoint*.go`.

It ports the remaining review items onto the live package:

- non-blocking `LOCK_EX|LOCK_NB` / `LOCKFILE_FAIL_IMMEDIATELY`
- 5s timeout so contention becomes an error (ingest already warns and
continues)
- subprocess test that a second acquire fails instead of hanging

Head: `1271102` on current main.

Co-authored-by: 勒布朗-詹姆斯 <318569545+waterbro-8@users.noreply.github.com>
…DC (G5) (#222)

## Summary

Follows the G5 sequence for #104: align `@bytefolk/mem-mcp@0.1.2` +
`io.github.bytefolk/mem-mcp`, then add an **OIDC** Registry publish
path.

- Rebase of draft #193 onto current `main` (conflicts resolved; keeps
24-asset checksums from #216).
- Does **not** create a tag, does **not** `npm publish`, does **not**
deprecate `@fullstack-ai-infra/mem-mcp@0.1.1`.
- Adds `.github/workflows/mcp-registry-publish.yml`
(`workflow_dispatch`) which:
1. refuses to run unless
`https://registry.npmjs.org/@bytefolk/mem-mcp/<version>` exists with
matching `mcpName`;
  2. logs in with `mcp-publisher login github-oidc`;
  3. publishes `io.github.bytefolk/mem-mcp`.
- Founder gate: GitHub Environment `mcp-registry` (create + restrict)
plus npm Trusted Publisher for `@bytefolk/mem-mcp` / workflow
`npm-publish.yml` / environment `npm-release` from #193.

Refs #104 #153. Successor to #193 (that PR is DIRTY against main).

## Sequence remaining after this PR merges

1. Founder: prove `@bytefolk` npm ownership
([.github#22](bytefolk/.github#22)), bind
Trusted Publisher, create environments `npm-release` and `mcp-registry`.
2. Maintainer: tag `v0.1.2` per `docs/maintainers/releasing.md` (not
this PR).
3. `npm-publish.yml` → `@bytefolk/mem-mcp@0.1.2`.
4. Dispatch `MCP Registry Publish` with `0.1.2`.
5. Paste Registry search receipt on #104.

## Test plan

- [x] `npm/package.json` is `@bytefolk/mem-mcp` `0.1.2` / `mcpName`
`io.github.bytefolk/mem-mcp`
- [ ] CI on this head
- [ ] Do not dispatch Registry publish until npm 0.1.2 exists

---------

Co-authored-by: 修雨 <huyizhou.hyz@alibaba-inc.com>
Co-authored-by: liyuanyang <liyuanyang@users.noreply.github.com>
Co-authored-by: waterbro-8 <318569545+waterbro-8@users.noreply.github.com>
@sun-970 sun-970 closed this Sep 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants