- The organization's
roadmap —
every major feature and release across PassionCode.ai as
RM-*tracks with owner, phase and state. It is the entry point: a task here that serves a track names it, and the track's status is edited only in the roadmap. - The PassionCode.ai knowledge base —
fabric-workspace/knowledge/in your clone (org-indexscripts/clone_all.shmakes it) or https://wiki.passioncode.ai/knowledge — at least its README, vision, principles and how-to-work. - This file, then the organization's CONTRIBUTING.md, then this repository's CONTRIBUTING.md (test first, one version, the CLA). This repository's files win where they differ from the organization's.
PassionCode.ai launcher: passioncode is the npm launcher that sets up PassionCode.ai on a computer
with one command (npx @passioncode-ai/passioncode@latest setup: the apps from their verified
releases, their MCP servers for every agent, the agent skills listed in family.json), checks it
(doctor) and keeps the skills current as one set (update). Members are vendored from their own repositories at a
pinned ref (family.json); Claude Code gets them from a local passioncode marketplace,
other agents through the ~/.agents/skills hub (README.md).
npm test # scripts/test.mjs enumerates tests for Node 18+; temp HOME and a fake claude CLI
npm run vendor # build payload/ from the members in family.json: a shallow clone of repo@ref
npm run vendor:release # the same, refusing any ref that is not a tag
PASSIONCODE_CHECKOUT_ROOT=<dir> npm run vendor # read members from local clones <dir>/<repo name> instead
npm run vendor -- --clone # ignore local clones (also PASSIONCODE_VENDOR_CLONE=1; PASSIONCODE_GIT_BASE replaces git@github.com:)
claude plugin validate ./payload --strictThere is nothing to install: the launcher has no dependencies and no lockfile, so npm ci
refuses to run here; npm test works on a fresh clone. Hosted workflows exist (validate.yml
runs npm test; release.yml releases and publishes on a v* tag when RELEASE_ENABLED and
PUBLISH_NPMJS are true). The repository is public, so they run outside the organisation's
spending cap; the local gate above is still the evidence, and a skipped run is reported as skipped.
MCP: the launcher serves no MCP server. It registers the installed apps' servers for every
agent (ADR-0003, ADR-0004) and doctor makes one proving call to each (README,
"Quick start → MCP").
lib/launcher.js(update, restore, status, uninstall),lib/setup.jsandlib/doctor.js(setup and doctor, ADR-0004),lib/catalog.js(the apps: release, asset, MCP command, proving call, onboarding),lib/releases.jsandlib/openpgp.js(verified downloads; the release keylib/release-key.asc),lib/mcp-probe.js(the proving call),bin/passioncode.js(the CLI),scripts/vendor.mjs(the payload),plugin/passioncode/(the self-update SessionStart hook:hooks/update-check.jsdecides,probe.jsasks npm,session-start.jsacts;trust.jsonnames the npm accounts allowed to publish).plugin/passioncode/skills/working-in-passioncode/: the organisation's working rules as a skill for every contributor's agent; its trigger and scenario evals are intest/evals/working-in-passioncode/, held to the Agent Skills standard bytest/skill.test.js.- Tests:
test/launcher.test.js(fakeclaudeintest/fake-claude.js),test/self-update.test.js(fakenpm/npxon PATH),test/vendor.test.js(a local bare remote),test/skill.test.js(the self plugin's skills). - Decisions: docs/adr/. What the launcher touches: SECURITY.md.
payload/is generated and never committed.
- One channel per agent: nothing is ever written to
~/.claude/skills. - Nothing of the person's is deleted: every move goes to
~/.passioncode/quarantine/and is undone bypassioncode restore. The one exception is the launcher's own output: an update keeps the release it installed and the one it replaced, and removes older~/.passioncode/releases/<version>directories (reinstallable from npm; lifecycle contract LC-11, LC-15). - A release vendors tagged bytes only (
npm run vendor:release, run byprepublishOnly), and the vendor step refuses credential-shaped strings. - The self-update never runs a version it has not attributed: only a pinned
@passioncode-ai/passioncode@<x.y.z>whose every npm maintainer and publisher is intrust.json(ADR-0002). - Members are PassionCode.ai products only. An agent someone builds for themselves is never added, even when it implements a Fabric protocol.
family.jsonnames no machine paths: local clones are an environment override (PASSIONCODE_CHECKOUT_ROOT), never a committed path.working-in-passioncodesummarises the knowledge base and org-index and points to them; it never restates a long document. A rule that changes there changes here in the next release, and so does the session-start line (plugin/passioncode/hooks/repo-rules.js, held bytest/repo-rules.test.js).- The licence is the organization's (Fabric ADR-0092):
AGPL-3.0-only OR LicenseRef-PassionCode-Commercial;LICENSE,COMMERCIAL-LICENSE.mdandCLA.mdbyte for byte the knowledge base templates (test/license.test.js). v0.1.5–v0.1.11 stay PolyForm, v0.1.4 and earlier MIT. - Shared registers are edited under a lease. docs/AGENT_SYNC.md
(generated from
.claude/agent-sync.jsonbyagent_sync.py setup; never edited by hand) lists the guarded files and the gate. Runagent_sync.py acquire <file>before editing one andagent_sync.py release <file>after, on every path including failure. The lease is a ref underrefs/agent-sync/leases/onorigin, so another contributor's agent sees it (git ls-remote origin 'refs/agent-sync/leases/*'); the record plane is local (fs), and.agent-sync/is git-ignored. No register here carries a "Next free ID" line, so nothing is reserved yet; a register that gains one is declared underidRegistersand taken withagent_sync.py reserve <REG>.
The org contract is knowledge/lifecycle.md.
Background footprint (LC-09). The launcher owns no launchd job, login item, port, MCP server or resident process. Everything it runs is started by a person or by Claude Code's SessionStart:
| What | Started by | Cadence | Cost | With no window | Stopped by |
|---|---|---|---|---|---|
passioncode setup / doctor |
a person or their coding agent | on demand | doctor: one short-lived process per registered MCP server (20 s cap), one GitHub API call per app (cached 30 min); setup: downloads, codesign/spctl, ditto, launchctl kickstart, open only for apps it installed |
nothing | exits; the MCP servers it started are stopped when their proving call returns |
passioncode update / restore / uninstall |
a person, or the background update below | on demand | one claude plugin call per member (180 s cap each) |
nothing | exits; ~/.passioncode/update.lock keeps one at a time (test/lifecycle.test.js) |
SessionStart hook plugin/passioncode/hooks/session-start.js |
Claude Code, matcher startup only (not resume, clear or compact) |
once per new session | 0.11–0.19 s, 2 processes (node + git remote get-url, 2 s cap), 5 s hook timeout; measured 2026-10-03 (lifecycle audit, passioncode-adapter §4) |
nothing | exits |
Probe hooks/probe.js → npm view |
the hook, detached | at most once a day (checkedAt older than 24 h), one at a time (probe.pending) |
1 node + 1 npm, one HTTPS call, 30 s cap | runs ≤ 30 s, then exits | its own timeout |
Background update npx @passioncode-ai/passioncode@<x.y.z> update --quiet |
the hook, only for a newer version from trusted publishers with auto-update on | at most once per 10 min (updatingSince), usually never |
as update |
runs to completion (tens of seconds) | exits; refuses if another update holds the lock |
| Observatory Log hooks (vendored member) | Claude Code: SessionStart on every start/resume/clear/compact (15 s cap), Stop after every turn (20 s cap) | per session and per turn | owned by passioncode-ai/project-observatory-dashboard (observatory/engine/skill/plugins/observatory-log/hooks/) |
nothing | exits |
Idle budget: zero — no process runs between sessions except a probe or update still inside its cap.
Build retention (LC-15). Output directories: payload/ (written by scripts/vendor.mjs,
replaced whole on each run — one copy) and *.tgz from npm pack (scripts/smoke-pack.mjs packs
into a temp dir and removes it). Keep no tarball: releases live on npm and GitHub. On a machine
the launcher installs to, ~/.passioncode/releases/ holds the current and the previous release;
the update prunes the rest itself (LC-15 after four updates only the current and the previous release remain). Cache cap: none beyond those; clean command: rm -rf payload *.tgz.
This repository is one of the passioncode-ai repositories. The org map and onboarding live in passioncode-ai/org-index
(private; readable by every org member):
- README: which repository owns what, and how they connect
- rules: branches, commits, CI, leases, secrets, handoffs
- ONBOARDING.md: setting up a new contributor's machine
Where this file is stricter than the shared rules, this file wins. A change to this repository's
role, dependencies or test command updates its row in org-index/repositories.json in the same change.
docs/backlog-sources.json declares this repository's canonical
local task sources and their vision goals. The common backlog contract
owns aggregation; the workspace backlog is a derived view.
Edit a task only in its canonical source under an agent-sync lease, retain stable IDs and
closure receipts, and declare any new source in the manifest. Do not edit generated task
status in the workspace or copy another repository's task into a second editable row.
Land the source change, then run node scripts/workspace.mjs sync from a Fabric checkout
(or use the scheduled sync); check the published source commit before calling it current.
In the same run: update this repository's docs with the change; if a cross-repository fact changed
(a product, a version, a plan row, a principle), update the page in fabric-workspace/knowledge/
that owns it — a launcher release changes the launcher's and its members' rows in products.md
and how-to-work.md; land both; publish (node scripts/workspace.mjs sync from a Fabric checkout)
or leave it to the scheduled sync. Leave a handoff with the exact next task.