Skip to content

Cross-project addressing — qualified wikilinks, a project roster, and planning roots

State: open/proposed

  • A task in another repo is addressed as <slug>:T-XXXX. The qualified form is accepted only on task.depends_on and, provisionally, milestone.tasks. Every other link stays local.
  • sdlc.yaml declares the project’s own slug: and a projects: roster of peers. The roster is the source of truth for which slugs exist and which repo each names. There is no project entity; a control-plane view of projects is a projection of the roster.
  • planning_roots: in the one root sdlc.yaml declares the control units inside a repo. Each unit carries its own corpus of any kind. Definition kinds inherit down the containment tree; a unit adds, never redefines. Work kinds never inherit. The module-graph rules are in D-XCXA-module-graph.
  • Ids resolve flat across roots; everything else resolves nearest-first. A duplicate basename across roots is an audit error. A slug-only or ambiguous target resolves from the linking entity’s root up its ancestors. slug:ID resolves through the roster.
  • Audit gains a third link state, unverified, for a qualified target whose peer is not present on this machine. It is never reported as unresolved and never fails the run unless --strict-peers is passed.
  • The engine needs no change. One engine root coordinates every repo; the work-order source namespaces ids by slug and rewrites qualified links so the engine sees one id space.

Cross-repo work exists today only as spawn-task-pr --target-repo. A task’s depends_on is a same-corpus wikilink. repos:, paths:, and checkout: locate checkouts but say nothing about which planning corpus lives where. There is one planning root per repo, though the ontological control plane already plans to read three units in this repo.

D-VSLI-distributed-work-runner-architecture leaves two open questions this decision answers: the addressing registry for peer projects, and federated planning corpora inside a monorepo with cross-corpus id uniqueness and wikilink resolution. D-3PDM-cross-project-data-model-adoption’s activation rule means a new field or entity needs a named producer and a named consumer before it ships.

Base-36 ids (D-0002-entity-identifier-shape) are unique within a prefix inside one corpus. Two repos can mint the same T-XXXX. Nothing today distinguishes them.

Addressing. A wikilink may carry a project qualifier: <slug>:T-XXXX or <slug>:T-XXXX-<slug-text>. A new pattern beside the entity wikilink pattern accepts it. The qualified form is valid only on task.depends_on and milestone.tasks; the milestone.tasks case is provisional, pending the open question below. parent_key, related, and every other link stay local. No entity filename carries the qualifier, so Obsidian resolves a qualified link to nothing and renders it inert rather than resolving it wrongly.

Roster. sdlc.yaml gains two keys:

slug: dev
projects:
polish:
repo: sksizer/polish
role: peer
sdlc-defs:
repo: sksizer/sdlc-defs
role: definition-source
docs_url: https://…

slug is this project’s name in the qualified form. projects maps each peer slug to its repo and role. A peer’s checkout is found through the existing repos:/checkout: resolution; a per-machine override goes in sdlc.local.yaml as projects.<slug>.path. projects: never implies execution scope, the same rule D-RSWP-standalone-repo-sweep-tool fixed for repos:. sdlc project verify-roster checks that each present peer’s own slug: equals its roster key and reports whether the peer is trusted.

No project entity. The roster is data in config, not an entity, because its consumers resolve links and locate checkouts; they do not author or review project records. If the ontological control plane wants a Project concept, it is a projection of this roster and never the other direction.

Planning roots. The one root sdlc.yaml may declare:

planning_roots:
ontological:
root: solutions/ontological/docs/planning
label: Ontological
agent-pants:
root: solutions/agent-pants/docs/planning
label: Agent Pants
scope: solutions/agent-pants
members:
- solutions/determined/experiences/agent-pants
KeyMeaningDefault
rootThe unit’s planning directory.Required.
labelDisplay name.The key.
scopeThe directory whose modules the unit takes by containment.The parent of docs/planning when root ends in it, else none.
membersExtra paths the unit claims, each with everything under it.None.

A sub-root holds its own <plural>/ directories. A second sdlc.yaml is never placed under a sub-root. Every planning root is a control unit and carries its own corpus of any kind. Definition kinds inherit down the containment tree: a unit sees its ancestors’ definitions and may add its own, but never re-declares or overrides an ancestor’s. Work kinds never inherit. Repo-wide definitions live at the project root because every unit inherits them. D-XCXA-module-graph holds the membership, claim, and inheritance rules. Minting sees the union of taken ids across roots. Generated artifacts (the index, reports) stay at the project root. sdlc project setup --planning-root <id> scaffolds a sub-root.

Resolution. An id resolves flat across the vault by basename; a duplicate basename across roots is an audit error. A slug-only or ambiguous target resolves nearest-first: the linking entity’s root, then each ancestor root in turn. A qualified slug:ID resolves through the roster.

Audit. A qualified link resolves against the peer’s local main when the peer is present. When the peer is absent, the link is unverified, a distinct state from unresolved. unverified is reported and does not fail the run, so a runner without the peers checked out stays green; --strict-peers promotes it to an error where the peers are expected. A cross-root duplicate basename is an error. A qualified link on any field other than the two allowed is a validation error.

Engine. The work-order source unions the roster’s present peers, namespaces ids as <slug>/T-XXXX, and rewrites qualified links so WorkOrderLink.target stays in one id space. Scope claims carry the slug as a path prefix, so the scope filter separates repos without a rule. A peer that is absent or untrusted yields a not-ready order whose reason names the peer, so the engine’s explain output shows why. One engine root and one coordination store cover every repo. Outcome reads the peer’s local main; scan never touches the network.

Qualify the link, not the id. Ids stay as D-0002-entity-identifier-shape fixes them. The qualifier lives on the edge, where the ambiguity is, and only on the two fields that carry cross-project intent. Restricting the fields keeps every other resolver unchanged.

A roster in config, not an entity. The activation rule in D-3PDM-cross-project-data-model-adoption asks for a producer and a consumer. The consumers here are link resolution, audit, and the work-order source. None of them needs a record with a lifecycle, and git already owns project identity. A config key is the smaller thing that satisfies the rule.

One root file for planning roots. Project root resolution walks up to the nearest sdlc.yaml. A second file under a sub-root would re-root every command run from there. Declaring roots in one place keeps resolution unambiguous.

Flat resolution for ids, nearest-first for the rest. Flat resolution matches how Obsidian resolves a vault, which is the reading surface for the corpus. A duplicate basename is then a real error, not a namespacing question. Slugs can repeat across units, so a target that names no id needs a tie-break; the nearest ancestor is the one inheritance already sees.

unverified is not unresolved. An absent peer is a fact about this machine. Reporting it as a broken link would make audit fail on every checkout that lacks a peer.

Zero engine changes. D-VSLI-distributed-work-runner-architecture makes the engine domain-blind. Namespacing ids in the source keeps the engine’s one-id-space contract intact, and one coordination store for all repos is simpler than routing by slug.

Mint ids as dev:T-XXXX everywhere. Rejected: it changes every filename and every existing link for a concern that only two fields have.

Add PJ-XXXX records with repo, role, and status. Rejected for now: no producer or consumer needs the record, and D-3PDM-cross-project-data-model-adoption says that shape accumulates zero instances. Revisit if the control plane needs to author project records.

Option C: A second sdlc.yaml per planning root

Section titled “Option C: A second sdlc.yaml per planning root”

Let each unit carry its own config. Rejected: root resolution becomes ambiguous, and inheritance needs one file that names the whole tree.

Option D: Per-repo coordination stores for the engine

Section titled “Option D: Per-repo coordination stores for the engine”

Route coordination by slug to each repo’s own ref namespace. Rejected: one engine root with one store gives cross-repo single activation for free. Remote authority sync for the engine’s store is a later question.

Easier. Cross-repo dependencies become explicit and auditable. The control plane reads planning units from config instead of a hard-coded list. Multi-repo dispatch is a source change, not an engine change. The same qualified form later addresses an npm-consumed definition corpus, rendered as an external link through the roster’s docs_url.

Binds future work. Every new link-bearing field decides whether it accepts the qualified form; the default is no. A peer that renames its slug: breaks every qualified link to it; verify-roster is the check.

Breaks nothing today. No existing corpus carries a qualified link or a second planning root.

PhaseChangeNeeds
P0slug:, projects:, planning_roots: in the config schema; verify-roster; conventions docthe verbs cascade work to land first, since it rewrites the config schema and its doc
P1qualified wikilink pattern; resolver; audit unverified; validation of allowed fieldsP0
P2union corpus loading; flat resolution; duplicate-basename error; union minting; setup --planning-rootP0
P2bnearest-first resolution; the add-never-redefine auditP2 and D-XCXA-module-graph’s control units
P3task next --project/--all; per-repo worktree and trust in the current orchestratorP1
P4sdlc work scan --all-projects; slug-prefixed scope; peer-unresolved reasonP3 and the engine adapter
P5npm-consumed definition corpus as a definition-source peerD-ME8T-definition-and-execution-packages
  • Whether milestone.tasks should accept qualified links at all, or whether a cross-repo milestone is better expressed as a milestone in each repo with depends_on between their tasks.
  • Whether the control plane’s Project concept stays a projection or earns an entity.
  • How a peer’s slug: rename is handled beyond detection.

← Back to Decisions