Skip to content

pkg.Resource rejects the canonical purl form its doc promises: "pkg:winget/Vim/Vim?scope=machine" fails as unknown package manager "pkg" #813

Closed

Description

@David-Noble-at-work

Feature: #459 — Provider value types and plan-time conversion (a string becoming a pkg.Resource).
Found: 2026-09-04 on DANOBLE-WD11-3 (Windows 11, arm64), the first writ deploy after #805 fixed the team
Scheduling (USER, 2026-09-04): super high priority — addressed as soon as thread 2 (resource management) completes, ahead of the rest of thread 3.
layer's manifests. Blocks the Windows deploy of the personal layer.

Observed

writ deploy noblefactor --dry-run, writ v0.1.0-dev.20260903040058-1-g966bdf91 (windows/arm64), team clone at
9c84a90, personal clone synced:

Error: manifest C:\Users\david-noble\.cache\devlore\snapshots\personal-27e53171af5e\Home\common.Windows\packages-manifest.yaml:
building "pkg:winget/Vim/Vim?scope=machine": phase "install": addNativeSoftwarePackages: plan.Provider.invocation:
pkg.Install: op.ActionPlanner.Plan: pkg.install: param "packages": slice index 0:
construct *pkg.Resource from string: pkg.Resource: unknown package manager "pkg"

The personal manifest (Home/common.Windows/packages-manifest.yaml):

packages:
  - name: pkg:winget/Vim/Vim?scope=machine

Mechanism

  1. cmd/lore/lore/builder.go addNativeSoftwarePackages passes every manifest package string to pkg.install
    unchanged, as the packages slice.
  2. pkg/op/provider/pkg/resource.go buildCandidate parses exactly two forms: manager:name[@version] and a bare
    name[@version]. It cuts at the first :, hands the prefix to platform.ResolvePurlType, and refuses when the
    prefix is not a manager. For a purl the prefix is the scheme, pkg, so every canonical purl is refused.
  3. The same file's doc comment promises a third form — value: "a string package name with an optional manager:
    prefix, or a canonical URI" (resource.go:101). The code never implemented it. buildCandidate emits the
    canonical form (purl.String() → pkg:apt/jq) as the resource URI, so the constructor cannot read what it writes.
  4. pkg/platform/purl.go has ParsePURL for the full grammar pkg:{type}/{namespace}/{name}@{version}?{qualifiers},
    and documents Namespace as "owner/group prefix (winget publisher, npm scope, …)". The purl form is the designed
    spelling for a winget package: publisher and name are two segments, and scope=machine has no other spelling.
  5. schema/packages-manifest.json describes packages[].name as "Package name" with examples gh, jq, ripgrep.
    The schema is behind the design on the same point.

So three surfaces disagree: the schema says bare names, the constructor accepts bare or manager:-prefixed names,
the platform and the manifests use purls.

Ruling (USER, 2026-09-04) — pending

Two ways to close the disagreement; the recommendation is the first:

  1. pkg accepts the canonical purl form through platform.ParsePURL: type resolved via ResolvePurlType,
    namespace and name joined as the manager expects (winget: Publisher.Name), version from @, qualifiers kept on
    the resource for the driver. The schema documents the purl form beside the bare and manager: forms.
  2. Manifests are rewritten to manager:name, and machine scope gets a new spelling in the manifest schema.

Acceptance

  • pkg.NewResource / pkg.DiscoverResource accept pkg:winget/Vim/Vim?scope=machine, pkg:brew/[email protected], and
    the existing brew:jq / jq forms; a malformed purl is refused with the parser's message
  • the constructor round-trips its own URI: DiscoverResource(env, r.URI()) yields the same catalog entry
  • schema/packages-manifest.json documents every accepted form of name
  • a unit test per form in pkg/op/provider/pkg, and a devlore-test fixture that plans a purl-named package
  • writ deploy noblefactor --dry-run on DANOBLE-WD11-3 passes the personal layer's Windows manifest

Workaround (USER, 2026-09-04) — in force until the fix lands

writ plans every layer from a snapshot worktree of the clone's HEAD (<layer>-<short hash> under the cache
directory), so a working-tree edit never reaches the planner and --allow-dirty only silences the dirty check. The
workaround is therefore a commit on the Personal repository, made against this issue:

  • Home/common.Windows/packages-manifest.yaml is set aside (renamed to packages-manifest.yaml.safe) or its entry
    is respelled in the form pkg accepts today, winget:Vim.Vim, which drops the scope=machine qualifier.
  • The VM's personal clone is synced afterwards, and writ deploy noblefactor runs without --allow-dirty.

The fix's plan carries the reversal as an acceptance box: the manifest returns to
pkg:winget/Vim/Vim?scope=machine on Personal's main, and the Windows deploy passes with it.

Also learned on the same test, tracked elsewhere

Activity

  1. David-Noble-at-work commented on Sep 8, 2026

    @David-Noble-at-work
    ContributorAuthor

    Met again 2026-09-08 on DANOBLE-WD11-3, the first deploy there since noblefactor-ops#182 and personal#174, on build 919b952:

    Error: manifest …\snapshots\personal-54de61d6ecb9\Home\common.Windows\packages-manifest.yaml: building "pkg:winget/Vim/Vim?scope=machine": … pkg.Resource: unknown package manager "pkg"
    

    Planning stops before a single link is made, so the whole personal layer is undeployable on Windows while its manifest carries the documented purl form. The detour from the last Windows test was applied again — a local commit on the writ-owned personal clone renaming the manifest to .safe — so the layer move could be verified there. The clone sits on workaround/813-manifest-form until this lands; git checkout main there undoes it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Epic:ResourceModelThe resource modelPriority:P1This cycleSeverity:HighA supported path is broken with no workaroundbugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions