Skip to content

chore(deps): agent-profile-materialize 0.19.2 keeps mounted skills byte-identical - #201

Merged
drewstone merged 2 commits into
mainfrom
chore/profile-materialize-0.19.2
Sep 7, 2026
Merged

chore(deps): agent-profile-materialize 0.19.2 keeps mounted skills byte-identical#201
drewstone merged 2 commits into
mainfrom
chore/profile-materialize-0.19.2

Conversation

@drewstone

@drewstone drewstone commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Change

  • @tangle-network/agent-profile-materialize 0.17.1 -> 0.19.2, exact pin like the other @tangle-network pins in package.json.
    pnpm-workspace.yaml already lists 0.19.2 under minimumReleaseAgeExclude (added for tests/runtime-consumer), so no exemption change.
  • What resolved before this change: package.json on main (1dffab1) declares exact 0.17.1, not a ^0.1.0 range.
    The ^0.1.0 range existed only from 5ec6309 (refactor(profiles): import published materializer + reword British→American #59) to 347de95, which widened it to ^0.5.1; af8d49a (feat(profiles): adopt current materializer contracts #80) pinned exact 0.9.2, and every later bump kept an exact pin, most recently 0.16.0 -> 0.17.1 at e94e87b.
    The container source b2bd3d0 carries the same exact 0.17.1 pin and the same root resolution.
    The root importer resolved 0.17.1(@tangle-network/agent-interface@1.9.0).
    Only the tests/runtime-consumer workspace, a devDependency, resolved 0.19.2(@tangle-network/agent-interface@2.3.1).
    So the bridge's serving path imported 0.17.1 while two materializer versions lived in one lockfile.
    After: one version, 0.19.2, in both importers.
    0.19.2 declares the peer @tangle-network/agent-interface: ^1.0.0 || ^2.0.0, so the root's 1.9.0 pin still satisfies it.
  • New test tests/skill-mount-bytes.test.ts with fixture tests/fixtures/profile-authoring-SKILL.md, a copy of discovery-lab skills/profile-authoring/SKILL.md: 3,862 bytes, sha256 7c505520ad7cc77e78c79d0488f19302ee7738270ca378bb3b253ce092b34131.
    It mounts the skill through provisionProfileWorkspace(req, null, 'claude-code', cwd), the call the claude backend makes, and asserts the written .claude/skills/profile-authoring/SKILL.md equals the source bytes.
    A second case mounts the mounted bytes again and asserts the same, which is the child-copies-the-mount case reproduced under Evidence (0.17.1: 3,866 then 3,874 bytes).
    No Lab artifact records those counts; the r1 smoke's live/native/profile-authoring-SKILL.md is an older 3,509-byte body.
  • tests/resume-rematerialization.test.ts: 0.19.1 marks the generated context file (CLAUDE.md) with a provenance marker and replaces its own output when the plan changes.
    The old test asserted the pre-0.19.1 refusal for exactly that case.
    It is now two cases: a changed plan replaces the materializer's own CLAUDE.md; an agent-appended CLAUDE.md is still refused with the same message and the edit survives.
    Two more cases pin the upgrade path itself: an unmarked CLAUDE.md as 0.17.1 wrote it, under a session that recorded the 0.17.1 plan digest, is adopted once when its body matches the new plan and refused when the agent appended to it.
    The mid-session profile swap stays refused at session binding (tests/session-profile-binding.test.ts).

Evidence

Reproduction outside the worktree, scratch npm installs of each version, materializeProfile(profile, 'claude-code', { skip: ['mcp'] }) on the Lab skill:

  • 0.17.1: source 3,862 bytes -> pass 1 3,866 (sha256 d53d313e…) -> pass 2 3,874 (sha256 fe3d97fa…).
    The only changed line is frontmatter line 3: description: "Author an exact AgentProfile…" becomes description: "\"Author an exact AgentProfile…\"".
  • 0.19.2: 3,862 -> 3,862 -> 3,862, sha256 7c505520… on all three.

Upgrade path, reproduced through the bridge's own provisionProfileWorkspace on a scratch checkout of main with 0.17.1 and then this worktree with 0.19.2 (profile: system prompt plus one instruction, claude-code):

  • 0.17.1 provisioned two workspaces and recorded plan digest sha256:563c4e49…; its CLAUDE.md starts at the body, no marker.
    One workspace then got an agent append (- codeword: heliotrope).
  • 0.19.2 computes sha256:035f9f01… for the same profile, so a session holding the 0.17.1 digest re-applies the plan.
    The unedited workspace was adopted and rewritten with the marked file.
    The edited workspace was refused: AgentProfile workspace materialization failed: Refusing to replace existing workspace file: CLAUDE.md — planned sha256:5119de67… != existing sha256:7fa6679b….

Commands in the worktree, N=/Users/drew/.nvm/versions/node/v22.23.2/bin:

  • $N/pnpm install --frozen-lockfile: Already up to date, 2 workspace projects (pnpm 11.25.0).
  • $N/pnpm exec tsc --noEmit: exit 0.
  • $N/pnpm exec vitest run: 1015 passed, 11 failed, 24 skipped of 1050 tests in 59 files.
    Both skill-mount tests pass; all 6 resume tests pass.
  • $N/pnpm run test:runtime-consumer: 1 of 1 pass.
  • The 11 failures are the same 11 on an untouched origin/main (1dffab1) worktree on this Mac: tests/docker-executor.test.ts (3), tests/failure-attribution.test.ts (1), tests/jail.test.ts (5), tests/pi-inference-isolation.test.ts (2); 11 failed, 172 passed, 10 skipped across those 4 files.
    Cause: macOS realpath expectations (/private/var/… vs /var/…, /System/Volumes/Data/home/…) and seatbelt-specific assertions; CI runs on ubuntu.
  • Lint: the repo has no lint script.
    The checked-in .ai-agent-hooks.mjs ran merge-conflict-markers and suspicious-secrets at commit, and mergeable-with-base plus pnpm vitest run tests/trace-emitter.test.ts at push.
  • Lockfile: the diff also carries libc: annotations pnpm 11.25 writes for rolldown and lightningcss binaries.
    pnpm@10.34.5 install --frozen-lockfile --lockfile-only --offline on the new lockfile exits 0, which is the docker/Dockerfile path (corepack prepare pnpm@10).

Other @tangle-network dependencies behind their latest published version (npm view), unchanged here:

  • root agent-interface 1.9.0 (latest 2.3.1)
  • tests/runtime-consumer: agent-eval 0.173.3 (latest 0.174.0), agent-runtime 0.194.0 (latest 0.196.0), sandbox 0.37.1 (latest 0.38.0)
  • current: agent-trace-contract 1.0.2; runtime-consumer agent-interface 2.3.1

Upgrading the running bridge in disco-fleet (not performed here)

How the container got b2bd3d0.
The container was created as docker run -d --name disco-fleet --privileged … node:24-bookworm sleep infinity with pnpm 11.18.0 via corepack (discovery-lab prereg/h2-topology/linux-host.md, step 1).
It holds no GitHub credential (Lab handoff 2026-09-03, standing decision 5), so source is copied in.
The bridge worktree is copied to a directory named by its merged commit, then pnpm install --frozen-lockfile and find … -name '._*' -delete for macOS AppleDouble files (linux-host.md, step 4).
The smoke's artifacts/meta-operator-recursion-smoke-r1/preflight/operator-run.json records bridgeCwd: /bridge-b2bd3d008febbcc31c9aec96f79c0db181bd3b2c, bridgePid: 1389, and the spec's bridgeUrl is http://127.0.0.1:8899 with ops/bridge-8899.env.

Steps once this PR merges at commit <sha>:

  1. On the Mac: git -C /Users/drew/code/cli-bridge fetch origin && git worktree add /Users/drew/webb/_wt/cli-bridge-<sha> <sha>.
  2. Copy without node_modules and .git: docker exec disco-fleet mkdir -p /bridge-<sha> && tar -C /Users/drew/webb/_wt/cli-bridge-<sha> --exclude node_modules --exclude .git -cf - . | docker exec -i disco-fleet tar -xf - -C /bridge-<sha>, then docker exec disco-fleet find /bridge-<sha> -name '._*' -delete.
  3. docker exec -w /bridge-<sha> disco-fleet pnpm install --frozen-lockfile (better-sqlite3 builds via allowBuilds), then docker exec -w /bridge-<sha> disco-fleet pnpm typecheck, then confirm docker exec -w /bridge-<sha> disco-fleet node -p "require('@tangle-network/agent-profile-materialize/package.json').version" prints 0.19.2.
  4. Stop the program loop (docker exec disco-fleet pgrep -f "^bash tools/program-loop.sh") and let live runs on the lane settle (Lab handoff 2026-09-03, restart order).
    Settling is required, not a courtesy: a session resumed across the upgrade whose agent edited CLAUDE.md fails its first turn on the new bridge (Limits below).
  5. Find the listener: docker exec disco-fleet ss -tlnpH | grep :8899.
    Confirm readlink /proc/<pid>/cwd is /bridge-b2bd3d008febbcc31c9aec96f79c0db181bd3b2c and its environ holds BRIDGE_PORT=8899.
    Kill that node process, its pnpm start parent, and the bash launcher, reading each pid first; never a pid below 2.
    Confirm the port is free and the data-directory lock <BRIDGE_DATA_DIR>/.cli-bridge-data-directory.pid from ops/bridge-8899.env is released; one bridge per data directory.
  6. Start: docker exec -d disco-fleet bash -c 'cd /bridge-<sha> && set -a; . /lab2/ops/bridge-8899.env; set +a; setsid nohup pnpm start >> /bridge-data-8899.log 2>&1 &'.
  7. Verify: GET /health answers (14 s last time); GET / advertises capabilities.profileMaterialization: cli-bridge.profile-materialization.v2, which agent-runtime's bridge executor preflights; the new listener's ps -o etimes= is in seconds; readlink /proc/<newpid>/cwd is /bridge-<sha>.
  8. Prove it live: run one profile that mounts skills/profile-authoring/SKILL.md and check the workspace's .claude/skills/profile-authoring/SKILL.md is 3,862 bytes with sha256 7c505520…; the next operator record must show bridgeCwd: /bridge-<sha>.
  9. Restart the loop.

Limits

  • The container was not touched; byte identity is shown on this Mac through the bridge's provisionProfileWorkspace path and on scratch installs of both versions.
  • 11 pre-existing macOS-only test failures listed above are unchanged by this PR.
  • The lockfile's new libc annotations were validated by pnpm 10.34.5 and 11.25.0 frozen installs, not by a Docker image build.
  • 0.19.1 changes plan digests for every harness whose context file is now marked, so a live session holding an appliedPlanDigest from 0.17.1 re-materializes once on its next turn; a context file 0.17.1 wrote is adopted when its body matches the new plan.
  • A 0.17.1 session whose agent edited CLAUDE.md is refused on its first post-upgrade turn (Refusing to replace existing workspace file: CLAUDE.md), because the unmarked, edited body no longer matches the planned body; resumed sessions must finish before step 5 or start a fresh workspace.
    Reproduced above and pinned by the two 0.17.1 cases in tests/resume-rematerialization.test.ts; not exercised live.

🤖 Generated with Claude Code

…te-identical

0.17.1 re-quoted a SKILL.md description that already carried quotes.
The Lab's skills/profile-authoring/SKILL.md (3,862 bytes) mounted as
3,866 bytes, and a copy of the mount as 3,874. 0.19.2 returns the
source bytes unchanged.

Add a test that mounts that skill through provisionProfileWorkspace on
the claude-code path and asserts byte identity, twice.

0.19.1 marks the generated context file and replaces its own output
when the plan changes, so the resume test now asserts replacement for
a generated CLAUDE.md and refusal for an agent-edited one.
A session resumed across the 0.17.1 to 0.19.2 upgrade holds the old plan
digest, so the new plan re-applies. Two cases pin what happens to the
unmarked context file 0.17.1 wrote: adopted once when its body matches the
new plan, refused when the agent appended to it. Measured with a scratch
0.17.1 install through provisionProfileWorkspace.

The skill-mount test header now names the scratch 0.17.1 reproduction as
the source of the 3,866 and 3,874 byte counts; no live run recorded them.
@drewstone
drewstone merged commit c154245 into main Sep 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant