Skip to content

Bootstrap Custo as the central distributor engine (MVP: AGENTS.md for module repos)#1

Draft
Marius Storhaug (MariusStorhaug) wants to merge 20 commits into
mainfrom
bootstrap-custo-distributor-mvp
Draft

Bootstrap Custo as the central distributor engine (MVP: AGENTS.md for module repos)#1
Marius Storhaug (MariusStorhaug) wants to merge 20 commits into
mainfrom
bootstrap-custo-distributor-mvp

Conversation

@MariusStorhaug

Copy link
Copy Markdown
Member

Summary

Bootstraps MSXOrg/Custo as the central managed-file distribution runtime for MSX initiatives,
replacing PSModule/Distributor as the engine. Ports the Distributor architecture, generalizes it
to be org-agnostic, and ships the first managed resource: a thin-pointer AGENTS.md for PSModule
module repositories.

What's included

  • README — positions Custo as the central engine, explains the source-of-truth separation
    (standards in MSXOrg/docs, initiative guidance in each initiative's docs repo, distribution
    runtime here).
  • AGENTS.md — repo-root agent onboarding pointer plus an operator runbook for enabling live
    rollout.
  • config/targets.json + schema — target organizations as configuration, not hardcoded in the
    sync script. Defaults to ["PSModule"] for the MVP but any initiative can add its org here.
  • scripts/Sync-Files.ps1 — ported from PSModule/Distributor, adapted to loop over
    configured target organizations instead of a hardcoded PSModule owner.
  • .github/workflows/sync-files.yml — daily scheduled sync + workflow_dispatch, same GitHub
    App auth pattern as Distributor (CUSTO_BOT_CLIENT_ID / CUSTO_BOT_PRIVATE_KEY secrets).
  • Repos/Module/AGENTS.md/AGENTS.md — the first managed file set: a thin pointer into
    MSXOrg/docs and PSModule/docs rather than duplicated process text, matching the pattern used
    by PSModule/Template-PSModule and PSModule/memory.

Ready vs. blocked for live rollout

Ready:

  • Sync engine, workflow, and MVP file set are in place and validated (PowerShell parse + JSON
    schema checks pass).
  • Architecture is org-agnostic — adding a new initiative is a config/targets.json change.

Blocked — no live sync can run yet:

  • CUSTO_BOT_CLIENT_ID / CUSTO_BOT_PRIVATE_KEY secrets are not configured on this repo. The
    psmodule-s-custo GitHub App (app id 1320343) is already installed on the PSModule
    organization, but its credentials live with PSModule/Distributor, not here.
  • Target module repositories still need their SubscribeTo custom property updated to include
    AGENTS.md.

See AGENTS.md in this repo for the exact operator runbook.

Next operator actions

  1. Provision CUSTO_BOT_CLIENT_ID / CUSTO_BOT_PRIVATE_KEY secrets on MSXOrg/Custo (reuse or
    re-key the psmodule-s-custo app).
  2. Confirm the app's installation covers the target module repositories.
  3. Set Type = Module and add AGENTS.md to SubscribeTo on repos that should receive it.
  4. Trigger Sync Managed Files via workflow_dispatch as a dry run before relying on the daily
    schedule.
  5. Separately (out of scope for this PR): update PSModule/docs Repository-Defaults.md to
    reference Custo instead of PSModule/Distributor once this is live.

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Copilot AI added 4 commits July 25, 2026 18:45
Rewrite README to explain source-of-truth separation between
MSXOrg/docs standards, initiative docs, and this distribution runtime.
Add repo-root AGENTS.md with operator runbook for enabling live rollout.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Port Sync-Files.ps1 from PSModule/Distributor, generalized to read
target organizations from config/targets.json instead of a hardcoded
'PSModule' owner. Add a JSON schema for the targets config.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Daily cron plus workflow_dispatch, mirroring PSModule/Distributor's
GitHub App auth pattern via CUSTO_BOT_CLIENT_ID/PRIVATE_KEY secrets.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
First managed resource: a thin pointer AGENTS.md for PSModule module
repos that references MSXOrg/docs and PSModule/docs instead of
duplicating process text. Repositories opt in via Type=Module and
SubscribeTo including 'AGENTS.md'.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@MariusStorhaug

Copy link
Copy Markdown
Member Author

*⚠️ Bug found: -Endpoint\ should be -ApiEndpoint*

During research on the GitHub module API surface (see PSModule/GitHub#653), I found that the current \PSModule/GitHub\ module uses -ApiEndpoint\ as the parameter name on \Invoke-GitHubAPI, not -Endpoint. PowerShell cannot resolve -Endpoint\ as a prefix abbreviation of -ApiEndpoint, so the three \Invoke-GitHubAPI\ calls in \scripts/Sync-Files.ps1\ will throw at runtime:

A parameter cannot be found that matches parameter name 'Endpoint'

Affected lines — search for -Endpoint\ in the script (3 occurrences: PR list GET, PR create POST, label POST).

Fix: change -Endpoint\ → -ApiEndpoint\ in all three calls.

This was fixed in the parallel branch (PR #2, now closed) at commit \c0ce191.

Related issues filed upstream:

Copilot AI and others added 16 commits July 25, 2026 19:18
Update Sync-Files.ps1 to use the current PSModule/GitHub parameter
name (-ApiEndpoint) for PR lookup, PR creation, and label assignment.
Behavior is unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Switch sync target resolution to scope-based discovery. Add all-access
mode (default) that enumerates all repositories visible to the app
installation token and reads Type/SubscribeTo custom properties per
repository. Keep organizations mode available for explicit filtering.
Update targets schema/json and README accordingly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add enterprise-level custom-property management for Type and
SubscribeTo. Allowed values are now generated from the Repos tree and
upserted via enterprise properties/schema endpoints before repository
sync starts. Make this behavior config-driven in targets.json and
document required app permissions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Introduce an explicit policy engine phase that runs enterprise controls
before repository sync. In all-access mode, discovery is now grouped by
organization to enforce enterprise -> organization -> repositories flow.
Update README pipeline wording to match the new order.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add an enterprise-policy API helper that uses CUSTO_ENTERPRISE_PAT
when present and falls back to GitHub App context otherwise. Wire
enterprise custom-property sync to this helper, pass the optional
secret into the workflow environment, and document the new secret.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Refactor Sync-Files to load and execute policy documents by layer
(enterprise -> organization -> repository), separating policy
capabilities from deployment configuration.

Add initial policy documents for enterprise custom properties,
enterprise rulesets, organization placeholder, and repository file
subscription service. Keep repository sync capability explicit in
policy documents.

Update enterprise API auth strategy to prefer GitHub App context and
fall back to CUSTO_ENTERPRISE_PAT only when app calls fail.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Introduce PolicyEngine/ with explicit separation between capabilities
and policies:
- Capabilities/{layer}/*.capability.json
- Policies/{enterprise}/{layer}/*.policy.json

Update sync runtime to load capabilities and validate policy docs
against supported capabilities, and resolve policy sets by enterprise
mapping from config/targets.json.

Migrate existing policy docs into the new hierarchy and update
documentation accordingly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Build out explicit configuration for the two enterprise policy
capabilities:
- repo-custom-property: required/values-editability options
- repo-rulesets: concrete starter ruleset payload config

Update policy execution to consume the extended custom-property
configuration fields and document the default enterprise policy files.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The auth-mode refactor misplaced the CapabilityCatalog parameter onto Sync-EnterpriseCustomPropertySchema (mandatory, never passed) while Invoke-PoliciesForScope referenced it without declaring it. Callers passed -CapabilityCatalog to a CmdletBinding function that rejected it, so the runtime failed on the first policy scope; even absent that, authMode was always null and fell back to github-app, so the enterprise-pat routing never executed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace line-based JSON comparison with recursive canonicalization (sorted keys) and a deep-subset check. Server-added fields and key ordering no longer register as drift, so enterprise property/ruleset PUTs only fire on real changes; array order remains significant.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Wrap the enterprise layer and each organization in try/catch so a single connect or traversal failure no longer aborts the whole fleet run; failures are recorded and counted (OrgsProcessed/OrgsFailed) and still fail the job at the end. Add Write-RunReport to emit a Markdown summary to GITHUB_STEP_SUMMARY (falls back to stdout locally).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Add JSON Schemas for capability/policy documents; add Pester suites covering canonical diffing, subset drift detection, policy-path resolution, authMode routing, and conformance of every shipped capability/policy doc. Add a CI workflow running Pester + PSScriptAnalyzer (error severity) on push/PR. Also make the enterprise property GET catch blocks explicit rather than silently swallowing errors.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Correct the file-set layout description (no org dimension) and clarify all-access discovery (org installations, org repos only). Document the CUSTO_ENTERPRISE_PAT blast radius and mitigations, and the WhatIf/report/test-gate safety model.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The Resolve-PolicyLayerPaths and authMode routing tests hardcoded a C: drive and \C:\Users\marst\AppData\Local\Temp, which fail on the Linux CI runner (drive qualifier resolution and empty TEMP). Use System.IO.Path temp helpers so the suite passes on Windows and Linux.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.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.

2 participants