Skip to content

fix(aprender-core): path-only dev-deps — break publish-time cycle (Refs #1514)#1515

Merged
noahgift merged 1 commit intomainfrom
feat/v0.32.0-publish-hygiene
May 5, 2026
Merged

fix(aprender-core): path-only dev-deps — break publish-time cycle (Refs #1514)#1515
noahgift merged 1 commit intomainfrom
feat/v0.32.0-publish-hygiene

Conversation

@noahgift
Copy link
Copy Markdown
Contributor

@noahgift noahgift commented May 5, 2026

Summary

aprender-core's dev-deps create a publish-time cycle:

  • `renacer` (= aprender-profile) workspace-pinned at 0.32.0
  • `entrenar` (= aprender-train) workspace-pinned at 0.32.0

Both depend on aprender-core at runtime → publishing aprender-core requires their 0.32.0 to exist on crates.io, which they can't because they require aprender-core 0.32.0 first. Cycle.

Fix

Override dev-dep entries to path-only (no version key). cargo strips path-only-no-version dev-deps from the published manifest.

```toml
renacer = { path = "../aprender-profile", package = "aprender-profile" }
entrenar = { path = "../aprender-train", package = "aprender-train" }
```

Local tests still resolve via the path; downstream consumers of the published crate don't see these deps at all.

Verification

  • `cargo publish -p aprender-core --dry-run --allow-dirty` ✓ clean (was failing)
  • `cargo test -p aprender-core --lib --no-run` ✓ compiles

Why this is the canonical fix

Per Cargo's published-manifest semantics, path-only-no-version dev-deps are stripped at publish time. Same pattern used by wasmtime, swc, and other monorepo projects.

The alternative (separate quantization crate per `qwen2.5-coder-showcase-demo.md §E.7`) is multi-PR architectural work; this 2-line change unblocks v0.32.0 cascade publish today.

Refs #1514

This is step 1 of the v0.32.0 cascade publish project. Subsequent steps:

  • Clean-room green on intel
  • Topological cargo publish (~13-19 user-facing crates)
  • Post-publish QA per `feedback_post_publish_qa_required.md`
  • CHANGELOG entry

🤖 Generated with Claude Code

…efs #1514)

aprender-core's dev-deps include `renacer` (= aprender-profile) and
`entrenar` (= aprender-train), both via `workspace = true`. The
workspace.dependencies entries pin `version = "0.32.0"`. cargo
publish includes the version in the published manifest, which means:

  Publishing aprender-core 0.32.0 requires entrenar 0.32.0 to exist on crates.io.
  Publishing entrenar 0.32.0 requires aprender-core 0.32.0 to exist on crates.io.

Cycle. Same class for renacer (= aprender-profile) ↔ aprender-core.

## Fix

Override at the dev-dependency level: change `workspace = true` to
path-only without a `version` key:

  renacer = { path = "../aprender-profile", package = "aprender-profile" }
  entrenar = { path = "../aprender-train", package = "aprender-train" }

cargo strips path-only dev-deps that have no version from the
published manifest. The runtime callers (anything depending on
aprender-core from crates.io) see no entrenar/renacer dep. Local
test runs still resolve via the workspace path.

## Verification

  $ cargo publish -p aprender-core --dry-run --allow-dirty
    Uploading aprender-core v0.32.0
    warning: aborting upload due to dry run     ← clean
  $ cargo test -p aprender-core --lib --no-run
    Finished `test` profile (compiles)

## Why this is the right fix

Per Cargo book: "Cargo will not allow path-only dependencies for
deps with version keys in the published manifest." Path-only-no-version
is the canonical pattern for internal dev-deps that don't ship to
crates.io. This is the same pattern many monorepo projects use
(e.g., wasmtime, swc).

The alternative (creating a separate quantization crate per the
qwen2.5-coder-showcase-demo.md §E.7 documented note) is much
larger architectural work that this single fix does not require.

## Five Whys

1. Why is there a publish-time cycle at all? aprender-core, aprender-train,
   and aprender-profile cross-reference each other for dev/integration
   tests; the workspace pins all internal deps at 0.32.0; cargo's
   published-manifest version requirement creates the cycle.
2. Why not break the cycle architecturally? Per docs/specifications/
   qwen2.5-coder-showcase-demo.md §E.7, that requires a separate
   quantization crate — multi-PR scope. The dev-dep cycle is solvable
   with a 2-line change.
3. Why are renacer/entrenar dev-deps in the first place? renacer
   provides syscall tracing for showcase benchmarks; entrenar provides
   InferenceMonitor for GH-305 integration tests. Both genuinely
   useful for local dev, but neither is needed by downstream
   crates.io consumers of aprender-core.
4. Why not just remove the dev-deps? Local tests use them. Removing
   would require porting those tests off the integration surface.
   Path-only preserves the local test capability.
5. Why now? Issue #1514: v0.32.0 cascade publish blocked by exactly
   this cycle. cargo publish dry-run was failing on aprender-core;
   fixing that unlocks the rest of the cascade.

Refs #1514

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@noahgift noahgift enabled auto-merge (squash) May 5, 2026 14:19
@noahgift noahgift merged commit 6ff85d1 into main May 5, 2026
11 checks passed
@noahgift noahgift deleted the feat/v0.32.0-publish-hygiene branch May 5, 2026 14:45
noahgift added a commit that referenced this pull request May 5, 2026
…oom compat (Refs #1514) (#1517)

Follow-up to PR #1515. The path-only-no-version fix worked for
`cargo publish --dry-run` but failed in the clean-room's GATE A0
strip:

  GATE A0: Strip local path deps (sed -i 's/path *= *"[^"]*"//g')
  GATE A1: cargo generate-lockfile
    error: dependency (entrenar) specified without providing a local
    path, Git repository, version, or workspace dependency to use

The clean-room's `strip_path_deps` is a naive sed that ONLY strips
the `path = "..."` part — leaving `entrenar = { package = "..." }`
which is an invalid Cargo.toml entry (no source).

`cargo publish` itself handles path-only-no-version dev-deps
correctly (drops them entirely), but the clean-room's heuristic
doesn't.

## Fix

Add a permissive version range alongside the path:

  renacer = { version = ">=0.27", path = "../aprender-profile", package = "aprender-profile" }
  entrenar = { version = ">=0.27", path = "../aprender-train", package = "aprender-train" }

After clean-room strip:
  renacer = { version = ">=0.27", package = "aprender-profile" }   ← valid

The version range `>=0.27` is permissive enough to satisfy any
currently-published aprender-profile / aprender-train (0.27.x
through 0.31.2). When v0.32.0 publishes, the range still satisfies.
The cycle is broken because publish doesn't require an EXACT 0.32.0
version on crates.io.

## Verification

  $ cargo publish -p aprender-core --dry-run --allow-dirty
    Uploading aprender-core v0.32.0 ✓
  $ # Simulated clean-room strip:
  $ sed 's/, *path *= *"[^"]*"//g' Cargo.toml
    → renacer = { version = ">=0.27", package = "aprender-profile" }   ← valid
    → entrenar = { version = ">=0.27", package = "aprender-train" }    ← valid

## Five Whys

1. Why did PR #1515's path-only-no-version fix fail in clean-room?
   Clean-room uses naive sed strip, not cargo's published-manifest
   logic. After strip: `entrenar = { package = "..." }` — invalid.
2. Why does clean-room use sed instead of cargo? Historical;
   simpler than reading published manifest. Out of scope to change.
3. Why permissive range >=0.27 vs >=0.30 or exact? 0.27 was the
   first stable aprender-{core,train,profile} version after the
   APR-MONO consolidation. Anything above resolves cleanly.
4. Why version + path vs path-only? After strip, just `version` +
   `package` remains — valid Cargo.toml entry. Cargo resolves the
   dev-dep via crates.io at test-time on the published crate;
   locally always uses path.
5. Why fix in a fresh PR vs amending #1515? #1515 already merged;
   one-PR-per-fix discipline per `feedback_falsifier_first_cascade_pattern.md`.

Refs #1514

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
noahgift added a commit that referenced this pull request May 5, 2026
Records the v0.32.0 cascade publish: 15 user-facing crates published
to crates.io in topological order via three release-engineering fix
PRs (#1515 dev-dep cycle break, #1517 clean-room compat, #1518 apr-cli
aliases.yaml in-crate copy).

Also documents the breaking aprender-rag lib rename
(trueno_rag → aprender_rag) per Issue #1510 / PR #1512.

User-facing additions for 0.32.0:
- apr pretrain --init polymorphic Qwen2.5-Coder-0.5B-Instruct fine-tune
- apr tokenize import-hf for HF BPE → aprender layout
- pv lint --strict-test-binding (PV-VER-002)

Closes #1514

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
noahgift added a commit that referenced this pull request May 5, 2026
…h + 4 release-engineering defects closed (#1520)

§58 records the parallel release-engineering track that landed during the §57 drift-sweep wait:
the v0.32.0 user-facing-crate cascade publish (Issue #1514 CLOSED) and the four hidden defects
it surfaced + closed.

User-facing crates now live on crates.io at v0.32.0:
  - aprender = "0.32.0"
  - aprender-rag = "0.32.0"
  - aprender-core = "0.32.0"
  - apr-cli = "0.32.0"

Defects closed during cascade (each in its own PR):
  - #1512 aprender-rag [lib] name = "trueno_rag" → "aprender_rag" BREAKING
  - #1513 aprender-orchestrate cmd_code 7→8 arg drift on emit_trace addition
  - #1515 aprender-core path-only dev-deps (publish-time cycle break)
  - #1517 aprender-core permissive version + path (clean-room sed-strip robustness)
  - #1518 apr-cli aliases.yaml in-crate copy (include_str scope fix)

Plus PR #1511 (pv-lint --strict-test-binding) closes §57.4's foreshadowed prevention rule.

5g.1 corpus retokenize (PID 2767124) at 62 shards / 16h19m wall (manifest pending).
Ship-% unchanged: MODEL-1=91%, MODEL-2=57%. §58 is the third hygiene amendment in a row;
§59 will record 5g.1 completion when manifest emits.

Refs: #1514

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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