Skip to content

feat(projects): a directory the daemon knows about, and step 3 of the resolution order (T39) - #10

Merged
haiquang9994 merged 14 commits into
masterfrom
t39-project-model
Aug 21, 2026
Merged

feat(projects): a directory the daemon knows about, and step 3 of the resolution order (T39)#10
haiquang9994 merged 14 commits into
masterfrom
t39-project-model

Conversation

@haiquang9994

Copy link
Copy Markdown
Collaborator

Roadmap task T39, the projects half. Sites move to T39a.

A directory becomes a project the daemon knows about, and step 3 of the resolution order —
"which PHP does this directory use?" — starts answering for the first time: it has had a
branch for a registered project since the beginning and never a row to hit.

Spec: docs/superpowers/specs/2026-08-22-t39-project-model-design.md
Plan: docs/superpowers/plans/2026-08-22-t39-project-model.md

What lands

  • core::manifest — one reader and one writer for mixengine.toml. The writer edits a
    toml_edit document, so an export keeps the comments, the key order and the [site] section
    it does not own.
  • core::projects — the table, the name rules, and the walk that used to live inside resolve.
  • project.create / list / show / update / delete / export, and mix project over them.
    import is a second name for create, at the CLI only.
  • runtime.uninstall refuses to remove a version a project pins, names the projects and their
    constraints, and --force crosses it.
  • A pin read from a manifest outranks the registered row, and mix project show says which of the
    two decided each version.

Three places the plan was wrong, and the tests that said so

  • update compared the row's holder by name, so every rename refused itself: by then the name
    in hand was the new one and the row still held the old. holder() returns the rowid and update
    compares that.
  • The plan described step 3's old loop as stopping at the nearest project. It does not — it falls
    through a project that pins nothing about the language asked for. Rewiring as written would have
    let a registered sub-package shadow the PHP of the repository around it. projects::find
    (nearest) and projects::pinning (nearest that pins this language) are now two questions over one
    walk.
  • The running-pool refusal could not be reached the way the plan assumed: the fixture's "PHP" is
    fakeservice, which refuses --nodaemonize and crash-loops to failed — a state an uninstall is
    entitled to remove. The row is recorded as running through declare::running, which is what the
    refusal actually reads.

One product bug CI found

mix project create /tmp/blog on macOS registered a directory that cd /tmp/blog && mix project show could not then find: /tmp is a symlink to /private/tmp and getcwd answers with the
second. paths::in_full now resolves symlinks off Windows, over the longest prefix that exists,
with the rest put back as it came — the same rule the Windows branch already followed, so a
spelling still does not change the first time the directory appears. canonicalize is right on
that side for the reason it is wrong on this one: the \?\ prefix is a Windows problem.

Verification

lint, test and bench green on all three runners. The shim budget the spec made D9 conditional
on was measured before the manifest reader was kept on the hot path: p50 3.95 ms, p90 5.09 ms
against 15 ms.

The shim bench decides this, and it passed: p50 3.95 ms, p90 5.09 ms
against a 15 ms budget, measured over /mnt/c in WSL. The narrow private
struct is gone; core::manifest is the one reader.
The walk lives in projects::pinning rather than in resolve, and it is a
different question from projects::find: a project silent about a
language does not shadow the project above it, which is the behaviour
this step has always had and the manifest walk's own rule.
@haiquang9994
haiquang9994 merged commit 7698e66 into master Aug 21, 2026
7 checks passed
@haiquang9994
haiquang9994 deleted the t39-project-model branch August 21, 2026 19:54
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