Read-access contract¶
Read-only adapter contract for Notion, GitHub, and approved supplemental sources (D-04 / G14). Authority and non-mutation rules come from the authority model and organization profile. Source scopes live in source-inventory.md.
Contract version: read-access.v1 (Gate C approved)
Tracking: hector-sanchez-eu/conductor#3
Brief: M1 brief
Do not unlock Bitwarden or call external APIs for M1 estate work until Gate C is approved.
Default Notion client for agents: official CLI ntn — see notion-access.md. Cursor Notion MCP is optional only.
Principles¶
- Adapters perform MCP
read(and optional localvalidateof payload shape). They never perform planauthorize. - Adapters must not mutate Notion, GitHub, or other authoritative state.
- Secret values resolve only from Bitwarden. Docs and config carry names and bindings only.
- Every response includes provenance, freshness, and completeness (
complete|partial|unavailable|denied|stale|malformed). - Permission filtering and privacy redaction apply before results leave the adapter boundary.
- Derived indexes are optional caches; they cannot override Notion/GitHub.
Secret names and provider bindings¶
| Secret / config name | Purpose | Required for |
|---|---|---|
CONDUCTOR_NOTION_READ_TOKEN |
Dedicated Notion read token | Live Notion estate reads |
CONDUCTOR_GITHUB_READ_TOKEN |
Dedicated GitHub read token | Optional unattended/service enumeration mode (M3 production hardening) |
CONDUCTOR_GITHUB_READ_ORGS |
Comma-separated approved GitHub organizations | Live GitHub enumeration scope |
CONDUCTOR_GITHUB_READ_USERS |
Comma-separated approved GitHub users | Live GitHub enumeration scope |
CONDUCTOR_GITHUB_READ_ALLOW_PRIVATE |
Explicit Gate C private-metadata policy switch | Private repositories in approved scopes |
CONDUCTOR_NOTION_INITIATIVES_DATABASE_ID |
Initiatives DB locator | Initiative queries |
CONDUCTOR_NOTION_WORKSTREAMS_DATABASE_ID |
Workstreams DB locator | Workstream queries |
CONDUCTOR_NOTION_PRODUCTS_DATABASE_ID |
Products DB locator | Product queries |
CONDUCTOR_NOTION_ASSETS_DATABASE_ID |
Assets DB locator | Asset queries |
CONDUCTOR_NOTION_DECISIONS_DATABASE_ID |
Decisions/Events DB locator | History queries |
CONDUCTOR_NOTION_ASSET_REPOS_DATABASE_ID |
Asset Repositories DB locator | Repo link queries |
CONDUCTOR_NOTION_ASSET_ENVS_DATABASE_ID |
Asset Environments DB locator | Deploy URL queries |
CONDUCTOR_NOTION_ASSET_EVIDENCE_DATABASE_ID |
Asset Evidence DB locator | Provenance/verification/promotion refs |
CONDUCTOR_NOTION_ASSET_RELATIONSHIPS_DATABASE_ID |
Asset Relationships DB locator | Per-edge relationship SoR |
CONDUCTOR_GATE_C_APPROVED |
Explicit Gate C enable flag | Live reads |
CONDUCTOR_READ_ALLOW_LIVE |
Operator live-read switch | Live reads |
CONDUCTOR_GITHUB_TOKEN |
Governance/proposal write path | Proposals only — not estate reads |
CONDUCTOR_NOTION_TOKEN |
Legacy write token | Legacy writes only (disabled by default) |
CONDUCTOR_LEGACY_WRITES_ENABLED |
Legacy mutation feature gate | Defaults false; unavailable in production |
CONDUCTOR_GITHUB_WEBHOOK_SECRET |
Webhook HMAC | Required; webhook fail-closed if absent |
Provider: Bitwarden Secrets Manager (organization binding). Missing secret → explicit failure; no host .env fallback as authoritative store.
Credential separation: estate read adapters must not fall back to CONDUCTOR_GITHUB_TOKEN, GH_TOKEN, GH_CLI_TOKEN, or CONDUCTOR_NOTION_TOKEN.
Database ID env vars are configuration locators (names/bindings), not secret values, but still organization-profile/adapter config—not packs.
Notion live reads require Gate C approval, CONDUCTOR_READ_ALLOW_LIVE, and the
dedicated Notion read token. GitHub supports two explicit modes:
- Operator mode (current): authenticated
ghCLI, constrained to approved owners and read-only commands. - Unattended service mode (deferred to M3): a dedicated read credential
supplied as
CONDUCTOR_GITHUB_READ_TOKEN.
The selected mode must be reported in provenance. There is no implicit fallback between modes.
Gate C approval covers the bounded discovery envelope in source-inventory.md. It does not require application names before enumeration. Source completeness is established by approved locators, exclusions, pagination exhaustion, privacy filtering, and explicit degraded-source states. Discovered application identities remain candidates until human review.
Approved operator discovery with gh¶
Hector approved use of the authenticated GitHub CLI for current operator-driven discovery on 2026-07-26:
- only read-only
gh repo listandgh api GEToperations may be used; - operations remain limited to the approved owners and metadata/content paths;
- the service adapter must not silently consume
GH_TOKEN,GH_CLI_TOKEN, or governance credentials; - evidence must disclose
auth_mode=gh-operatorand that the operator credential may be broader than a future service credential.
Operator discovery satisfies M1 source-enumeration evidence. Whether Conductor needs unattended GitHub access, and its least-privilege credential, is an M3 runtime/deployment decision.
Permission requirements¶
Notion¶
| Capability | Required | Forbidden |
|---|---|---|
| Query approved databases | Yes | Create/update/delete pages |
| Retrieve page properties | Yes | Modify properties |
| List children for approved parents | Optional | Archive/move databases |
Token must be least-privilege read against approved DB IDs only. Broader workspace admin is out of Gate C scope unless Hector explicitly expands.
GitHub¶
| Capability | Required | Forbidden |
|---|---|---|
| List repos in approved orgs/users | Yes | Create repos, issues, PRs, releases |
| Read repository metadata | Yes | Push, merge, admin |
| Read README / selected metadata files | Optional, privacy-filtered | Write contents |
Proposal/write tokens used by governance tooling are a separate binding and must not be reused to imply write permission in the estate read path.
Snapshot and provenance envelope¶
Every adapter result wraps records as:
yaml
source: notion | github | supplemental
source_locator: <db id, repo, url>
fetched_at: <iso8601>
snapshot_version: <opaque>
completeness: complete | partial | unavailable | denied | stale | malformed
provenance:
- kind: notion-page | github-repo | human | deploy-url
ref: <id or url>
records: []
errors: []
retry: {recommended: true, after: "<seconds>"} # optional
rate_limit: {status: 429, retry_after: "<seconds>"} # optional
Pagination: adapters must surface exhaustion. If has_more is true without a cursor, set completeness=partial and explain. Never report complete for truncated pages.
Rate limits: on HTTP 429, return unavailable (or partial if some pages succeeded) with retry / rate_limit metadata—never fabricate missing rows.
Transport errors, malformed JSON, and malformed records map to unavailable / malformed / partial as documented in adapter tests.
Freshness and stale behavior¶
| Condition | Behavior |
|---|---|
| Successful fetch | completeness=complete (or partial); record fetched_at |
| Cached index older than policy TTL while SoR unreachable | completeness=stale; cite cache age; do not present as live |
| Source denied (403) | completeness=denied; empty records |
| Source down / timeout | completeness=unavailable |
| Payload fails schema validation | completeness=malformed; quarantine record |
Callers must not treat stale, denied, unavailable, partial, or malformed as a complete estate.
Privacy filtering and redaction¶
- Reads receive authenticated
identity,active_role, and an organization privacy policy. - Each record must carry
privacy_class. Unclassified records fail closed unless the source has an explicitly approved default in the Gate C inventory. - Adapter redacts records the active role may not see.
- Private GitHub repository metadata is withheld unless Gate C policy explicitly allows private-repo indexing for that role.
- Services client artifacts: metadata only unless Gate C expands scope.
- Secret values never appear in adapter output.
Application wiring¶
The preparation tool prepare_estate_context uses build_read_adapters (permission-filtered, fail-closed). Fixture mode is for developer tests only and is not baseline evidence.
Legacy mutation surface¶
Pre-M1 Notion/GitHub/Calendar mutation MCP tools and schema bootstrap are disabled unless CONDUCTOR_LEGACY_WRITES_ENABLED=true. That gate must not be enabled in production. The GitHub webhook fails closed when CONDUCTOR_GITHUB_WEBHOOK_SECRET is absent, and refuses writes when legacy mode is off.
Non-mutation assurance¶
| Control | Requirement |
|---|---|
| HTTP methods | GET/HEAD (and Notion search/query equivalents) only in read adapters |
| Code review | Read adapter modules must not import write orchestrator mutation helpers |
| Tests | Contract tests assert no create/update/delete calls |
| Config | Read token separate from any write-capable token when possible |
The existing write orchestrator under service/conductor/integrations/ is out of M1 steady-state scope (known M0 drift). M1 read adapters are separate modules.
Supplemental sources¶
Approved supplemental reads (deployed hosts, workspace pointers) follow the same envelope. They never become Asset SoR; they only feed candidate provenance.
Gate C approval checklist (access portion)¶
Hector approves Gate C access rules when commenting Conductor-Gate-C: approve after accepting this contract and source-inventory.md scopes.