Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b753a48a4 | ||
|
|
5c83c89bae | ||
|
|
2a223767b3 |
@@ -10,8 +10,6 @@ COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
|
||||
COPY apps/appservice/package.json ./apps/appservice/
|
||||
COPY packages/ ./packages/
|
||||
COPY plugins/ ./plugins/
|
||||
# the root prepare script runs scripts/install-hooks.mjs on install
|
||||
COPY scripts/ ./scripts/
|
||||
RUN pnpm install --frozen-lockfile
|
||||
COPY . .
|
||||
RUN pnpm turbo run build --filter @mosaicstack/mosaic-as...
|
||||
|
||||
@@ -882,3 +882,12 @@ Objective: for alpha 0.0.50, the release cannot publish, report, or display work
|
||||
### Out of scope
|
||||
|
||||
The canonical dispatcher/control-plane vertical slice (work graph, execution attempts, fenced leases, typed check-in, independent verifier dispatch) is decided post-alpha (SDLC-D-033, option B). Multi-pipeline verification certificates (SDLC-D-034 option B) are post-alpha. Full AF-1..AF-4 objective matrices and Mission Control portfolio surfaces are post-alpha.
|
||||
|
||||
## Official CLI Capability and Tool Migration Workstream (T78)
|
||||
|
||||
Normative contract on integration trunk `next`:
|
||||
[docs/requirements/cli-capability-migration.md](./requirements/cli-capability-migration.md):
|
||||
migrates agent-facing operations from directly invoked scripts into documented, first-class
|
||||
`mosaic` CLI command groups, together with the central-registry resolver, capability catalog,
|
||||
adapter boundary, and phased legacy-tool-tree decommission the migration requires. The contract
|
||||
carries its own implementation hold and delivery stages.
|
||||
|
||||
+3
-1
@@ -12,7 +12,9 @@ design; scoping one requires its own PRD section or requirements doc plus
|
||||
review.
|
||||
|
||||
Phases are product phases. The in-flight platform workstreams (KBN-100/101
|
||||
kanban SOT implementation, FCM #758, FCOM #766, TESS, RI #1275, and the other
|
||||
kanban SOT implementation, FCM #758, FCOM #766, TESS, RI #1275, T78 CLI
|
||||
capability migration
|
||||
([requirements](./requirements/cli-capability-migration.md)), and the other
|
||||
Part II contracts in the PRD) run as parallel tracks under their own issues
|
||||
and are prerequisites where noted.
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
- [Active task rollup](TASKS.md) — orchestrator-owned work state; workers do not modify it.
|
||||
- [MVP mission manifest](MISSION-MANIFEST.md) — control-plane mission rollup; activity and status remain under its authorized owner.
|
||||
- [Documentation catalog and truth audit](reports/documentation/2026-08-10-docs-catalog-audit.md) — complete baseline inventory, evidence labels, broken-link clusters, and migration recommendations.
|
||||
- [CLI capability migration requirements](requirements/cli-capability-migration.md): T78 official CLI capability and tool migration contract, normative contract with implementation hold (M0).
|
||||
|
||||
## Protected current authority and executable books
|
||||
|
||||
|
||||
@@ -0,0 +1,748 @@
|
||||
---
|
||||
kind: spec
|
||||
status: active
|
||||
source_of_truth: true
|
||||
---
|
||||
|
||||
# Official Mosaic CLI Capability and Tool Migration
|
||||
|
||||
- **Workstream:** T78
|
||||
- **Status:** active requirements contract, implementation held by the M0 gates
|
||||
- **Decision authority:** Jason Woltje
|
||||
- **Design owner:** Vision
|
||||
- **Integration trunk:** `next`
|
||||
|
||||
This contract is authoritative only on the integration trunk `next`. Branch copies are proposals.
|
||||
Publication does not authorize implementation until the M0 milestone, task-graph, interface, and
|
||||
partition gates pass.
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
Migrate agent-facing operations from directly invoked scripts into documented, first-class command
|
||||
groups in the existing TypeScript and Node.js `mosaic` CLI. The CLI becomes the stable interface
|
||||
for operators, agents, the webUI, future seat containers, and future `mosaicd` execution.
|
||||
|
||||
The mission also phases out the installed `~/.config/mosaic/tools` script surface. Existing scripts
|
||||
may remain private compatibility adapters only while measured consumers still require them.
|
||||
|
||||
## 2. Product alignment
|
||||
|
||||
Items 1 through 3 implement PRD D8 and D12:
|
||||
|
||||
1. The CLI is the primary execution surface.
|
||||
2. The webUI uses Gateway APIs backed by the same official capability contracts.
|
||||
3. A missing official capability is built before a webUI bypass is accepted.
|
||||
|
||||
This contract adds one explicit extension beyond D8 and D12: no harness, skill, or agent receives a
|
||||
separate business-logic path around the CLI and Gateway capability contract.
|
||||
|
||||
This contract does not replace the fleet north star, issue `#1382`, the fleet configuration
|
||||
contract `#758`, the exact fleet communications contract `#766`, or future container and `mosaicd`
|
||||
specifications. It defines the interfaces those tracks consume.
|
||||
|
||||
## 3. Fixed decisions
|
||||
|
||||
| ID | Decision |
|
||||
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| T78-D1 | Extend the existing official TypeScript and Node.js `mosaic` CLI. A second Python or shell entrypoint is forbidden. |
|
||||
| T78-D2 | Expose documented groups such as `mosaic git`, `mosaic comms`, and `mosaic ci`. A generic public `mosaic tools` passthrough is forbidden. |
|
||||
| T78-D3 | Resolve homes, endpoints, sockets, tool locations, and runtime paths through the central registry and one typed resolver. Commands do not hard-code them. |
|
||||
| T78-D4 | One rootless container per seat is the target sandbox. It has a read-only root filesystem, no container-runtime socket, and lifecycle through future `mosaicd`. |
|
||||
| T78-D5 | Dispatch is per-site. Localhost `orch-01` alone dispatches USC-seat implementation. Homelab `orch-01` alone dispatches homelab-seat implementation and homelab-owned surfaces. |
|
||||
| T78-D6 | Tmux and fleet-comms remain temporary communications adapters behind a transport-neutral CLI contract. |
|
||||
| T78-D7 | Decommissioning is phased and mechanically enforced. Removal requires zero measured consumers and a discriminating planted-reference control. |
|
||||
|
||||
Derived security boundary:
|
||||
|
||||
- `~/.mosaic/tools` is canonical working source during migration. It is not automatically trusted
|
||||
runtime installation state.
|
||||
- Reviewed source is promoted into installed or packaged runtime artifacts.
|
||||
- A multi-writer brain-repository push must not silently replace credential-bearing executable code
|
||||
used by every seat.
|
||||
|
||||
## 4. Explicitly rejected alternatives
|
||||
|
||||
| Alternative | Rejection reason |
|
||||
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| Separate Python CLI | Creates a second contract, release path, and policy surface. |
|
||||
| Public `mosaic tools <script>` passthrough | Preserves script names and paths as the API instead of defining capabilities. |
|
||||
| CLI allowlists as the sandbox | Parser allowlists do not isolate files, credentials, processes, networks, or container control. |
|
||||
| Execute the synced working tree as the final runtime | A brain push would become host-wide code execution authority. |
|
||||
| Big-bang script rewrite and deletion | Mature queue, identity, credential, and uncertainty behavior would be changed without parity evidence. |
|
||||
| Tmux-shaped communications API | It would force future Matrix or native transports to preserve tmux concepts. |
|
||||
|
||||
## 5. Terminology
|
||||
|
||||
- **Central registry:** the schema-v1 `config.json` authority filed in issue `#1382`.
|
||||
- **Registry resolver:** the typed reader that validates and resolves central-registry values.
|
||||
- **Capability catalog:** the typed inventory of public capability identifiers and behavior. It is
|
||||
not the central registry.
|
||||
- **Capability policy:** data that maps verified actor and lane identity to allowed capabilities and
|
||||
scopes.
|
||||
- **Local adapter:** a temporary in-process or private-script implementation used before `mosaicd`
|
||||
is available.
|
||||
- **Broker adapter:** the future client transport to `mosaicd` outside the seat container.
|
||||
- **Installed legacy tree:** `~/.config/mosaic/tools`.
|
||||
- **Canonical working source:** `~/.mosaic/tools` during the migration period.
|
||||
- **Runtime artifact:** reviewed package or installed bytes actually executed by a seat.
|
||||
|
||||
## 6. Public CLI grammar
|
||||
|
||||
### CLI-REQ-001: First-class command groups
|
||||
|
||||
The official help surface MUST register domain groups directly:
|
||||
|
||||
```text
|
||||
mosaic git ...
|
||||
mosaic comms ...
|
||||
mosaic ci ...
|
||||
```
|
||||
|
||||
Future domains MAY include `infra`, `identity`, and other reviewed capability families. They MUST
|
||||
NOT appear through a generic script dispatcher.
|
||||
|
||||
### CLI-REQ-002: Stable command shape
|
||||
|
||||
New capability commands use this grammar:
|
||||
|
||||
```text
|
||||
mosaic <domain> <resource> <verb> [target] [options]
|
||||
```
|
||||
|
||||
The first pilot freezes these paths:
|
||||
|
||||
```text
|
||||
mosaic git issue list
|
||||
mosaic git issue view <number>
|
||||
mosaic git issue comment <number> --input <path|->
|
||||
```
|
||||
|
||||
Capability identifiers are independent from display text:
|
||||
|
||||
| Command | Capability ID | Class |
|
||||
| -------------------------- | ------------------- | ---------------- |
|
||||
| `mosaic git issue list` | `git.issue.list` | read |
|
||||
| `mosaic git issue view` | `git.issue.view` | read |
|
||||
| `mosaic git issue comment` | `git.issue.comment` | bounded mutation |
|
||||
|
||||
Renaming a command path does not silently rename its capability identifier. Either change requires a
|
||||
versioned compatibility decision.
|
||||
|
||||
### CLI-REQ-003: Common targeting options
|
||||
|
||||
The pilot supports:
|
||||
|
||||
- `--instance <name>` for the configured provider instance.
|
||||
- `--repo <owner/name>` for the provider repository.
|
||||
- `--format <table|json>` for output selection.
|
||||
- `--correlation-id <id>` for a caller-supplied valid identifier. Omission generates one.
|
||||
- `--idempotency-key <key>` for mutations. Omission generates one and returns it.
|
||||
|
||||
An instance may be inferred only when the registry has exactly one valid instance for that domain.
|
||||
A repository may be inferred only from a validated current repository declaration and an
|
||||
unambiguous canonical remote. Ambiguity fails closed and names the missing field.
|
||||
|
||||
No public option forces local compatibility mode when policy selected broker mode. A caller cannot
|
||||
downgrade the execution boundary.
|
||||
|
||||
### CLI-REQ-004: Mutation input
|
||||
|
||||
`git.issue.comment` reads its body from `--input <path>` or stdin with `--input -`. The CLI MUST:
|
||||
|
||||
1. reject a missing or empty body.
|
||||
2. apply a documented byte limit before provider access.
|
||||
3. never place the body in process arguments, diagnostics, or audit metadata.
|
||||
4. compute a body digest for read-back verification without exposing the body.
|
||||
5. avoid automatic retry after an uncertain provider mutation.
|
||||
|
||||
### CLI-REQ-005: Structured result envelope
|
||||
|
||||
JSON output uses one versioned envelope:
|
||||
|
||||
```ts
|
||||
interface CapabilityResultV1<T> {
|
||||
schemaVersion: 1;
|
||||
capabilityId: string;
|
||||
status: 'succeeded' | 'invalid' | 'denied' | 'failed' | 'uncertain' | 'unavailable';
|
||||
executionMode: 'local-adapter' | 'mosaicd';
|
||||
identityTrust: 'local-asserted' | 'runtime-verified';
|
||||
correlationId: string;
|
||||
idempotencyKey?: string;
|
||||
target: Record<string, string | number | boolean | null>;
|
||||
data?: T;
|
||||
diagnostics: Array<{
|
||||
code: string;
|
||||
message: string;
|
||||
field?: string;
|
||||
retryable: boolean;
|
||||
}>;
|
||||
audit:
|
||||
| { authority: 'mosaicd'; recorded: true; eventId: string }
|
||||
| { authority: 'none'; recorded: false; localEventId?: string };
|
||||
}
|
||||
```
|
||||
|
||||
`target` and `diagnostics` contain no credentials or unbounded provider body. Table output is a
|
||||
human view of the same result and cannot carry a different verdict.
|
||||
|
||||
### CLI-REQ-006: Exit behavior
|
||||
|
||||
| Exit | Meaning |
|
||||
| ---: | --------------------------------------------------------------------------- |
|
||||
| 0 | `succeeded` |
|
||||
| 2 | `invalid`: invalid input, invalid configuration, or unsupported schema |
|
||||
| 3 | `denied` by capability or scope policy |
|
||||
| 4 | `failed` with a confirmed non-success outcome |
|
||||
| 5 | `uncertain`, including a mutation whose provider result cannot be confirmed |
|
||||
| 6 | `unavailable`, including missing broker, credentials, or required adapter |
|
||||
|
||||
A provider HTTP success alone is insufficient. The adapter validates the expected response shape.
|
||||
A mutation that may have landed but lacks confirmation returns exit 5 and is never described as
|
||||
failed or safe to retry. For a provider-native idempotent mutation, manual reconciliation MAY retry
|
||||
the same key. For `uncertain-no-retry`, help directs the caller to a read-back check and forbids
|
||||
mutation retry.
|
||||
|
||||
### CLI-REQ-007: Help and discovery
|
||||
|
||||
The capability catalog generates or validates:
|
||||
|
||||
- `mosaic --help` command-group listing.
|
||||
- group and command help.
|
||||
- stable capability identifiers.
|
||||
- machine-readable capability discovery.
|
||||
- documentation tables.
|
||||
- policy-generation inputs.
|
||||
- tests that reject undocumented public commands and orphaned capabilities.
|
||||
|
||||
## 7. Central registry resolver
|
||||
|
||||
### CFG-REQ-001: One distinct resolver
|
||||
|
||||
Implement one exported resolver named `MosaicRegistryResolver` or another name explicitly approved
|
||||
in the contract review. It MUST NOT be named `ConfigService`. The existing
|
||||
`packages/mosaic/src/config/config-service.ts` exports `ConfigService` for SOUL, USER, and TOOLS
|
||||
content and remains a separate concern.
|
||||
|
||||
### CFG-REQ-002: Frozen schema consumption
|
||||
|
||||
The resolver consumes schema v1 from issue `#1382` without creating parallel keys. Every key is
|
||||
optional. The exact v1 surface is:
|
||||
|
||||
- `$schema`, with the known marker `mosaic-config-v1`.
|
||||
- `mosaicHome`, reserved, null, and without a v1 consumer.
|
||||
- `brainHome`, default `~/.mosaic`.
|
||||
- `instances.gitea.<name>.url`.
|
||||
- `fleet.socket`.
|
||||
- `harnessConfig.pi.agentDir`.
|
||||
- `harnessConfig.claude.configDir`.
|
||||
- `harnessConfig.claude.secureStorageDir`.
|
||||
|
||||
Credential values, model and effort defaults, and `fleet.rosterPath` are forbidden. A non-null
|
||||
`mosaicHome` value fails validation because v1 reserves the field without implementing relocation.
|
||||
An absent or null `$schema` is interpreted as v1, the exact `mosaic-config-v1` marker is accepted,
|
||||
and every other non-null marker fails before value resolution.
|
||||
|
||||
Absent or null values select the framework default. A `~` path prefix expands at read time and is
|
||||
never rewritten into the user file. Unknown top-level and nested keys warn loudly and are ignored
|
||||
for rolling-version compatibility. Every warning and machine-readable diagnostic names the full
|
||||
ignored key path, so a typo is visible at every read.
|
||||
|
||||
Fail-closed read behavior applies to invalid JSON, a failed C1 version check, a known key with an
|
||||
invalid type or value, and a present but empty or invalid override. An optional
|
||||
`mosaic registry validate` lint mode MAY reject unknown keys for operator validation, but the normal
|
||||
resolver read path does not. This top-level group is separate from the existing `mosaic config`
|
||||
commands backed by `ConfigService`.
|
||||
|
||||
### CFG-REQ-003: Resolution precedence
|
||||
|
||||
For each supported value, resolution follows exactly:
|
||||
|
||||
1. the schema-defined `MOSAIC_<KEY>_OVERRIDE` environment override.
|
||||
2. validated `config.json` value.
|
||||
3. one centralized framework default, when the key defines a default.
|
||||
|
||||
A present override always wins. An empty or invalid override fails and does not fall through to the
|
||||
file or default. `$schema` and reserved `mosaicHome` have no environment override. Consumed values
|
||||
use this collision-free mapping:
|
||||
|
||||
| Registry key | Environment override |
|
||||
| --------------------------------------- | ---------------------------------------------------------- |
|
||||
| `brainHome` | `MOSAIC_BRAIN_HOME_OVERRIDE` |
|
||||
| `fleet.socket` | `MOSAIC_FLEET_SOCKET_OVERRIDE` |
|
||||
| `harnessConfig.pi.agentDir` | `MOSAIC_HARNESS_CONFIG_PI_AGENT_DIR_OVERRIDE` |
|
||||
| `harnessConfig.claude.configDir` | `MOSAIC_HARNESS_CONFIG_CLAUDE_CONFIG_DIR_OVERRIDE` |
|
||||
| `harnessConfig.claude.secureStorageDir` | `MOSAIC_HARNESS_CONFIG_CLAUDE_SECURE_STORAGE_DIR_OVERRIDE` |
|
||||
| `instances.gitea.<name>.url` | `MOSAIC_INSTANCES_GITEA_<NAME>_URL_OVERRIDE` |
|
||||
|
||||
Gitea instance names match `[a-z][a-z0-9-]*`. The override name uppercases the instance name and
|
||||
maps hyphen to underscore. Underscores are not valid in source instance names, so two valid names
|
||||
cannot flatten to the same override.
|
||||
|
||||
A value without a valid result fails before adapter or provider access. Invalid known URLs, socket
|
||||
names, paths, and value types fail closed. Unknown keys follow CFG-REQ-002.
|
||||
|
||||
### CFG-REQ-004: Typed provenance
|
||||
|
||||
Every resolved value carries non-secret provenance:
|
||||
|
||||
```ts
|
||||
type RegistryValueSource = 'override' | 'registry' | 'framework-default';
|
||||
|
||||
interface ResolvedRegistryValue<T> {
|
||||
key: string;
|
||||
value: T;
|
||||
source: RegistryValueSource;
|
||||
schemaVersion: 1;
|
||||
}
|
||||
```
|
||||
|
||||
Machine-readable diagnostics include the full path of every ignored unknown key. Diagnostics may
|
||||
name a known key and source class. They do not emit credential values or unrelated configuration.
|
||||
|
||||
### CFG-REQ-005: Bootstrap and path safety
|
||||
|
||||
Registry discovery is the fixed path `~/.config/mosaic/config.json`. It has no v1 search path and no
|
||||
alternate location. The file is the one user-updatable path inside `~/.config/mosaic` and is
|
||||
protected by a deny-wins upgrade carve-out. Upgrades never overwrite user edits.
|
||||
|
||||
This fixed bootstrap avoids circular dependence on reserved `mosaicHome`. A seat container reads its
|
||||
own internal `~/.config/mosaic/config.json`, supplied by the container topology, rather than a host
|
||||
path or a relocation flag. Path values are expanded, normalized, validated, and tested under at
|
||||
least two distinct home roots.
|
||||
|
||||
No command embeds home directories, script locations, provider endpoints, seat paths, or tmux socket
|
||||
names outside the resolver and its reviewed defaults.
|
||||
|
||||
### CFG-REQ-006: Schema evolution
|
||||
|
||||
A new key requires:
|
||||
|
||||
1. a named consumer.
|
||||
2. a `#1382` schema amendment.
|
||||
3. joint ACK from the frozen-schema and resolver-contract custodians until handoff, recorded by
|
||||
custodian-authored commits rather than relayed tokens alone.
|
||||
4. parser, invalid-input, default, and two-root tests.
|
||||
5. documentation in the same reviewed change.
|
||||
|
||||
Speculative keys are forbidden.
|
||||
|
||||
### CFG-REQ-007: Joint freeze evidence
|
||||
|
||||
The v1 resolver contract is jointly frozen:
|
||||
|
||||
- Fred, frozen-schema custodian, accepted C1 and C3 through token
|
||||
`CLI-T78-REGISTRY-FREEZE ACCEPT`, then accepted amended C2 through token
|
||||
`CLI-T78-REGISTRY-C2 ACCEPT`.
|
||||
- Homelab `orch-01`, issue and resolver-contract custodian, accepted C1 and C3 and supplied the
|
||||
adopted C2 rolling-version amendment in the fleet-comms repository, message
|
||||
`sites/usc/20260827T004509Z__to-vision__from-homelab.orch-01__683e4c.md`, blob
|
||||
`35a7c4c1e54eb9196abeef3135d211ee1dfc46db`.
|
||||
|
||||
Durable lane provenance is recorded in the Mosaic brain repository at
|
||||
`fleet/lanes/cli-migration/registry-freeze-evidence.md` and the independent custodian-authored
|
||||
`fleet/lanes/cli-migration/registry-freeze-fred-ack.md`. Schema evolution after this freeze still
|
||||
follows CFG-REQ-006.
|
||||
|
||||
## 8. Capability catalog and policy
|
||||
|
||||
### CAP-REQ-001: One typed catalog
|
||||
|
||||
Each capability definition records:
|
||||
|
||||
```ts
|
||||
type CapabilityEffect = 'read' | 'bounded-mutation' | 'privileged-mutation';
|
||||
|
||||
interface CapabilityDefinitionV1 {
|
||||
id: string;
|
||||
commandPath: readonly string[];
|
||||
effect: CapabilityEffect;
|
||||
targetSchema: string;
|
||||
inputSchema: string;
|
||||
outputSchema: string;
|
||||
credentialClass: string | null;
|
||||
requiredScopes: readonly string[];
|
||||
auditRequired: boolean;
|
||||
timeoutMs: number;
|
||||
idempotency: 'read' | 'required-key' | 'provider-native' | 'uncertain-no-retry';
|
||||
adapterId: string;
|
||||
deprecation: 'active' | 'deprecated' | 'removed';
|
||||
}
|
||||
```
|
||||
|
||||
The catalog is data consumed by the parser, help, policy, documentation, and tests. Command handlers
|
||||
must not maintain independent copies of these facts.
|
||||
|
||||
### CAP-REQ-002: Policy is not parser logic
|
||||
|
||||
Capability grants map verified actor identity and lane to capability IDs and resource scopes. They
|
||||
are data. A named seat receives no authority from its name alone.
|
||||
|
||||
The user-editable central registry is placement and endpoint configuration, not authorization
|
||||
policy. It MUST NOT contain lane grants or let a seat self-grant capability scope. Target authority
|
||||
lives in the `mosaicd` control-plane store outside seat containers and returns a policy revision and
|
||||
digest with every decision.
|
||||
|
||||
Before `mosaicd`, local compatibility mode may evaluate a package-owned policy for behavior and test
|
||||
parity, but it reports locally asserted identity and makes no broker-grade authorization claim. Mode
|
||||
selection is declared by topology and policy, never inferred from broker availability. A missing or
|
||||
unhealthy required broker returns `unavailable`. It never falls back to local mode.
|
||||
|
||||
A capability using a shared, service, operator, or admin credential is broker-only. Local mode may
|
||||
use only the acting seat's own credential against a registry endpoint. Privileged infrastructure,
|
||||
merge, deployment, identity, authorization, and secret-management cutover requires `mosaicd`. A
|
||||
future policy-store key or broker endpoint still requires CFG-REQ-006 and the `mosaicd` topology
|
||||
contract.
|
||||
|
||||
### CAP-REQ-003: Identity trust
|
||||
|
||||
CLI arguments and ordinary environment variables are actor hints, not authorization identity. The
|
||||
local adapter reports that identity is locally asserted and MUST NOT claim broker-grade
|
||||
authorization. `mosaicd` derives or verifies actor identity from the authenticated seat runtime.
|
||||
|
||||
### CAP-REQ-004: Positive and denied controls
|
||||
|
||||
Every capability test includes:
|
||||
|
||||
1. an allowed request with expected result.
|
||||
2. a denied request differing only in the relevant lane or scope.
|
||||
3. a malformed target or configuration denial.
|
||||
4. a credential-redaction assertion.
|
||||
5. a verdict-discrimination control that proves the test can fail.
|
||||
|
||||
## 9. Adapter and broker contract
|
||||
|
||||
### EXE-REQ-001: One capability request
|
||||
|
||||
```ts
|
||||
interface CapabilityRequestV1 {
|
||||
schemaVersion: 1;
|
||||
capabilityId: string;
|
||||
actorHint?: { seat?: string; lane?: string };
|
||||
target: Record<string, string | number | boolean | null>;
|
||||
arguments: Record<string, string | number | boolean | null>;
|
||||
correlationId: string;
|
||||
idempotencyKey?: string;
|
||||
}
|
||||
```
|
||||
|
||||
Credential values and unbounded comment bodies are not serialized into audit-safe request metadata.
|
||||
Body content travels through a bounded private input channel appropriate to the adapter.
|
||||
|
||||
### EXE-REQ-002: Local compatibility adapter
|
||||
|
||||
The local adapter MAY call a reviewed in-process implementation or a private script adapter. It
|
||||
MUST preserve existing queue guards, wrapper-first behavior, credential resolution, response
|
||||
validation, and mutation uncertainty. It reports `executionMode: local-adapter` and
|
||||
`identityTrust: local-asserted`.
|
||||
|
||||
Private child adapters receive bodies and credentials only through stdin, owner-only temporary
|
||||
files, or inherited file descriptors, never child-process arguments. Captured child stderr, shell
|
||||
trace, and diagnostics are inside the redaction boundary. Local results always use
|
||||
`audit: { authority: 'none', recorded: false }`. A local event identifier is not authoritative
|
||||
audit evidence.
|
||||
|
||||
The local adapter is compatibility, not a sandbox or authorization claim.
|
||||
|
||||
### EXE-REQ-003: `mosaicd` broker adapter
|
||||
|
||||
The broker adapter sends the same logical request to `mosaicd` outside the seat container.
|
||||
`mosaicd` owns:
|
||||
|
||||
- authoritative seat identity, recorded in audit from the derived runtime identity rather than
|
||||
`actorHint`.
|
||||
- capability and scope authorization.
|
||||
- credential resolution.
|
||||
- operation execution.
|
||||
- output sanitization.
|
||||
- audit persistence.
|
||||
- bounded timeout and cancellation behavior.
|
||||
|
||||
A contradictory `actorHint` produces a diagnostic and never replaces the derived actor. Broker
|
||||
results report `identityTrust: runtime-verified`. `audit.recorded: true` is valid only after
|
||||
`mosaicd` confirms persistence and returns its event ID. Consumers verify authoritative evidence
|
||||
against the broker trail, not the seat-produced envelope alone.
|
||||
|
||||
The transport and endpoint are supplied by immutable container topology and the reviewed central
|
||||
registry contract. No command hard-codes a daemon socket.
|
||||
|
||||
### EXE-REQ-004: Packaged implementation boundary
|
||||
|
||||
The initial TypeScript layout is:
|
||||
|
||||
- `packages/mosaic/src/central-registry/` for `MosaicRegistryResolver`, schema, and provenance.
|
||||
- `packages/mosaic/src/capabilities/` for catalog, request, result, policy interfaces, and tests.
|
||||
- `packages/mosaic/src/capabilities/adapters/local/` for temporary local adapter modules.
|
||||
- `packages/mosaic/src/capabilities/adapters/mosaicd/` for the broker client seam.
|
||||
- `packages/mosaic/src/commands/git.ts`, with later first-class domain files following the same
|
||||
command pattern.
|
||||
|
||||
Remaining script implementations may be promoted under `packages/mosaic/framework/tools/` as
|
||||
private packaged adapters during transition. Their installed paths are resolver-owned and are not
|
||||
public command contracts. No production adapter imports or executes source from the brain working
|
||||
tree as the final path.
|
||||
|
||||
### EXE-REQ-005: Container boundary
|
||||
|
||||
The representative seat container has:
|
||||
|
||||
- one seat identity.
|
||||
- rootless execution.
|
||||
- read-only root filesystem, with explicit bounded writable mounts.
|
||||
- a read-only internal `~/.config/mosaic/config.json` supplied by topology.
|
||||
- no host credential tree.
|
||||
- no shared host or fleet tmux socket.
|
||||
- a dedicated per-seat tmux socket only for one named, reviewed temporary adapter with a stated
|
||||
removal stage.
|
||||
- no Docker, Podman, or other container-runtime socket.
|
||||
- no installed legacy tool tree mount.
|
||||
- network access limited to declared capability paths.
|
||||
|
||||
Container implementation is outside this mission. Contract and compatibility tests are inside it.
|
||||
|
||||
## 10. Communications portability
|
||||
|
||||
### COM-REQ-001: Transport-neutral public contract
|
||||
|
||||
Public communications capabilities use logical addresses, messages, correlation IDs, delivery
|
||||
status, and adapter diagnostics. Tmux pane, socket, retry, and draft details stay below the public
|
||||
contract.
|
||||
|
||||
### COM-REQ-002: Transitional semantics
|
||||
|
||||
The tmux adapter preserves the measured `rc=2` behavior: content reached a pane as a draft, so the
|
||||
operation is not retried automatically. Fleet-comms preserves durable cross-site message identity
|
||||
and acknowledgment behavior.
|
||||
|
||||
### COM-REQ-003: Future transport replacement
|
||||
|
||||
A Matrix or native transport implementation passes the same contract tests. Callers do not change
|
||||
command paths, capability IDs, or result interpretation when the adapter changes.
|
||||
|
||||
## 11. Canonical source and runtime integrity
|
||||
|
||||
### SRC-REQ-001: Reviewed baseline
|
||||
|
||||
The F11 baseline is commit `5be5825`. Inventory report `585f214`, code review `3fe8de7`, and
|
||||
security review `e270098` are the M0 evidence. Both reviews found no blocker.
|
||||
|
||||
### SRC-REQ-002: Required M1 corrections
|
||||
|
||||
Before expanding direct execution from the working tree:
|
||||
|
||||
1. fix the `check-helper-drift.sh` environment assignment that suppresses version diagnostics.
|
||||
2. strip 20 dangling Excalidraw `node_modules` symlinks.
|
||||
3. add `tools/**/node_modules/` to the brain `.gitignore`.
|
||||
4. keep the reviewed `package-lock.json` as the reproducible dependency contract.
|
||||
5. correct the baseline report's misleading path-count headline.
|
||||
6. move `ci-publish-watch.sh` credential headers from process arguments to curl stdin
|
||||
configuration when that suite is changed.
|
||||
|
||||
### SRC-REQ-003: Source is not installation
|
||||
|
||||
Runtime code is loaded from reviewed package or installed artifacts, not directly from a mutable
|
||||
multi-writer checkout as the final design. Any transitional direct execution requires:
|
||||
|
||||
- a protected-path review rule.
|
||||
- an accepted digest anchored outside the synced tree in reviewed package metadata or Stack source.
|
||||
- verification before execution, including every credential-helper invocation.
|
||||
- a periodic verifier whose mismatch alert reaches a human.
|
||||
- a stated removal point.
|
||||
|
||||
### SRC-REQ-004: Credential helper integrity
|
||||
|
||||
The host-wide git credential helper and its accepted pin cannot be replaceable by the same synced
|
||||
commit. Transition requires an independently anchored verifier and alert. Final state moves the
|
||||
helper into the reviewed runtime installation or another explicitly protected location.
|
||||
|
||||
## 12. Migration and decommission
|
||||
|
||||
### MIG-REQ-001: Consumer census
|
||||
|
||||
Inventory every direct caller of `~/.config/mosaic/tools`, grouped as:
|
||||
|
||||
- skills and guides.
|
||||
- hooks and generated harness configuration.
|
||||
- systemd units and timers.
|
||||
- launchers and provisioning.
|
||||
- tests and CI.
|
||||
- direct agent commands.
|
||||
- private tool-to-tool calls.
|
||||
- production consumers.
|
||||
|
||||
Each census run creates a fresh randomized planted legacy reference at a unique path and is valid
|
||||
only when the detector reports that run's exact plant. Every host at every site still running the
|
||||
installed legacy tree is censused independently. An empty result without the fresh control, or a
|
||||
zero from only one host, is not evidence.
|
||||
|
||||
### MIG-REQ-002: Risk-ordered waves
|
||||
|
||||
Migrate in this order:
|
||||
|
||||
1. read-only status, health, list, and view.
|
||||
2. bounded CI and communications.
|
||||
3. issue, pull-request, and milestone mutation.
|
||||
4. credentialed infrastructure.
|
||||
5. merge, deployment, identity, authorization, and secret management.
|
||||
|
||||
Each wave proves contract parity before consumer cutover. Waves 1 through 3 may use local mode with
|
||||
acting-seat credentials. Wave 4 cutover is broker-only when it uses a shared or service credential.
|
||||
Wave 5 cutover is always broker-only and begins only after the M6 `mosaicd` boundary gate passes.
|
||||
|
||||
### MIG-REQ-003: Protected consumers
|
||||
|
||||
- M365 credentials, AD status, and six production consumers remain Peggy-owned until exact signoff
|
||||
and timer-aware tests.
|
||||
- Fleet-doctor, seat-service, Woodpecker extras, and their units remain Veronica-owned until exact
|
||||
replacement proof and named handoff.
|
||||
- Brain guards are excluded from wholesale removal.
|
||||
- The active A2 hold applies to `tools/seat-service/` and
|
||||
`fleet/bin/launch-seat-claude.sh` only.
|
||||
- Fleet configuration issue `#758` retains its own normative contract and delivery DAG. T78 does
|
||||
not re-scope or absorb its missing `inspect` and `validate` verbs. T78 measures and consumes the
|
||||
stable fleet surface only after `#758` completion or an explicit owner handoff.
|
||||
|
||||
### MIG-REQ-004: Compatibility and deprecation
|
||||
|
||||
Compatibility shims are private and time-bounded. Each shim:
|
||||
|
||||
- names its public replacement.
|
||||
- preserves existing safety behavior.
|
||||
- emits a machine-detectable deprecation diagnostic without corrupting JSON output.
|
||||
- has a measured consumer and removal issue.
|
||||
- cannot be used to add new direct callers.
|
||||
|
||||
### MIG-REQ-005: Final removal
|
||||
|
||||
The installed `~/.config/mosaic/tools` script surface is removed only after:
|
||||
|
||||
1. all active consumers use official capabilities.
|
||||
2. the census reports zero with a firing planted control.
|
||||
3. Constitution and wrapper-first gates are mechanically enforced by the CLI path.
|
||||
4. systemd units are regenerated, daemon-reloaded, re-enabled, and behavior-tested.
|
||||
5. fleet-doctor state is preserved.
|
||||
6. clean install, upgrade, rollback, and stale-install tests pass.
|
||||
7. user, admin, developer, API, and migration documentation is current.
|
||||
|
||||
## 13. Testing requirements
|
||||
|
||||
### TST-REQ-001: Resolver
|
||||
|
||||
- exact schema-v1 valid fixture.
|
||||
- absent, null, exact-v1, and unknown-non-null `$schema` cases.
|
||||
- unknown top-level and nested key warnings with full-path diagnostics.
|
||||
- `mosaic registry validate` lint rejection of the same unknown-key fixture.
|
||||
- invalid URL, path, socket, and type failures.
|
||||
- every precedence branch, including present-empty and present-invalid override denial without
|
||||
fallback.
|
||||
- two valid roots.
|
||||
- container topology with a read-only internal registry and no host registry path.
|
||||
- no credential value accepted or emitted.
|
||||
- control proving the invalid fixture fails.
|
||||
|
||||
### TST-REQ-002: Capability catalog
|
||||
|
||||
- command and capability ID uniqueness.
|
||||
- every public command documented.
|
||||
- no orphan catalog record.
|
||||
- parser, policy, help, and docs consume the same definition.
|
||||
- unauthorized lane and scope denial.
|
||||
- unknown capability denial.
|
||||
- topology-selected mode never falls back when the required broker is unavailable.
|
||||
- shared, service, operator, and admin credential classes reject local mode.
|
||||
|
||||
### TST-REQ-003: Pilot
|
||||
|
||||
- issue list and view against a valid configured instance.
|
||||
- invalid instance and repository denial.
|
||||
- comment success with provider response-shape and body-digest confirmation.
|
||||
- comment denial before provider access.
|
||||
- post-request uncertainty without retry, plus provider-native same-key and
|
||||
`uncertain-no-retry` read-back reconciliation cases.
|
||||
- credential, cookie, token, comment-body, child-argv, captured-stderr, and shell-trace redaction.
|
||||
- local results prove `identityTrust: local-asserted` and `audit.recorded: false`.
|
||||
- broker-stub results prove derived-identity precedence and reject unconfirmed
|
||||
`audit.recorded: true`.
|
||||
- user-editable endpoint changes cannot redirect a shared or service credential.
|
||||
- local-adapter and broker-stub request/result seam parity at M3.
|
||||
- live local-adapter and `mosaicd` contract parity at M6.
|
||||
|
||||
### TST-REQ-004: Migration
|
||||
|
||||
- fresh randomized consumer-census plant detected independently on every affected host and site.
|
||||
- compatibility diagnostics in table and JSON modes.
|
||||
- systemd timer and restart behavior.
|
||||
- production M365/AD consumer probes.
|
||||
- fleet-doctor digest-state preservation.
|
||||
- clean install, upgrade, rollback, stale install, and greenfield operation.
|
||||
- representative container without legacy tools mounted.
|
||||
- representative container mounts no shared or fleet tmux socket, and any temporary tmux exception
|
||||
uses only the named adapter's dedicated per-seat socket.
|
||||
|
||||
### TST-REQ-005: Delivery gates
|
||||
|
||||
Every source card requires focused tests, repository quality gates, independent code review,
|
||||
security review for authorization, credentials, transport, or integrity surfaces, reviewed squash
|
||||
PR to `next`, terminal-green CI, and linked-issue closure.
|
||||
|
||||
## 14. Documentation requirements
|
||||
|
||||
The workstream updates in the same delivery sequence:
|
||||
|
||||
- official CLI help.
|
||||
- `docs/PRD.md` workstream pointer.
|
||||
- `docs/ROADMAP.md` parallel-track entry.
|
||||
- `docs/SITEMAP.md` requirements link.
|
||||
- user guide commands and deprecation behavior.
|
||||
- administrator configuration, migration, and recovery.
|
||||
- developer architecture, capability authoring, schemas, and adapter contracts.
|
||||
- API and machine-readable result schemas.
|
||||
- release notes.
|
||||
- T78 program-map and unified-roadmap records.
|
||||
|
||||
No command is public until its help, structured output, authorization behavior, and documentation
|
||||
are present.
|
||||
|
||||
## 15. Delivery stages
|
||||
|
||||
| Stage | Scope | Exit gate |
|
||||
| ----- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
||||
| M0 | Register mission, reconcile ownership, establish and review source baseline, publish requirements and tracking | Reviewed contract merged, dedicated milestone and task graph present |
|
||||
| M1 | Inventory consumers, normalize baseline, freeze registry resolver, capability catalog, policy, and runtime-integrity contracts | Typed interfaces and migration census reviewed |
|
||||
| M2 | Implement resolver, catalog, common result envelope, and adapter interface | Contract and two-root tests green |
|
||||
| M3 | Deliver pilot issue list, view, and comment | Allowed and denied controls, uncertainty behavior, docs, review, CI |
|
||||
| M4 | Migrate waves 1 through 3, prepare wave 4 private adapters without shared-credential cutover | Per-suite owner handoff and parity evidence |
|
||||
| M5 | Cut eligible consumers and generate harness policy | No new direct references, compatibility callers measured |
|
||||
| M6 | Prove representative container and `mosaicd` seam, then cut over shared-credential wave 4 and all wave 5 capabilities | Boundary, authorization, audit, and parity tests green |
|
||||
| M7 | Remove installed legacy script tree | Zero callers, migration and rollback evidence, docs and release gates complete |
|
||||
|
||||
## 16. Workstream acceptance
|
||||
|
||||
T78 completes only when:
|
||||
|
||||
1. the official TypeScript CLI exposes documented first-class capability groups.
|
||||
2. the central registry resolver and capability catalog are single typed authorities.
|
||||
3. two-root and container-topology tests prove no command-path hard-coding.
|
||||
4. authorization has allowed and denied situational evidence.
|
||||
5. agent-visible output, logs, and process arguments contain no credential values.
|
||||
6. local and `mosaicd` modes share one request and result contract and report their mode honestly.
|
||||
7. a representative rootless seat container performs granted operations without legacy tools,
|
||||
host credentials, or a container-runtime socket.
|
||||
8. tmux and fleet-comms can be replaced without changing public communications callers.
|
||||
9. the legacy consumer census reaches zero with a discriminating control.
|
||||
10. the installed `~/.config/mosaic/tools` script surface is removed.
|
||||
11. independent review passes for every source partition.
|
||||
12. all PRs are squash-merged to `next`, terminal CI is green, and linked issues are closed.
|
||||
|
||||
## 17. Contract-freeze status
|
||||
|
||||
The architecture inputs are frozen for independent review:
|
||||
|
||||
1. The central-registry resolver has joint C1, amended C2, and C3 approval.
|
||||
2. User-editable `config.json` is not authorization policy. Target grant authority belongs to
|
||||
`mosaicd`. Local mode is explicitly non-authoritative.
|
||||
3. Registry, capability, and adapter source boundaries are packaged TypeScript modules. Brain tools
|
||||
remain working source and temporary private adapters, not the final runtime contract.
|
||||
4. Issue `#758` remains an independent dependency and is not re-scoped into T78.
|
||||
|
||||
Provider tracking remains operationally blocked until the `orch-01` Mosaic Stack credential slot is
|
||||
minted. This does not weaken the contract or authorize implementation before reviewed publication.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,351 +0,0 @@
|
||||
# Roll-up Projection Contract (S2 contract 8)
|
||||
|
||||
Status: DRAFT — awaiting ratification (webui-audit S2, contract 8 of 9).
|
||||
Authority: `native-kanban-sot.md` §8 (A1 amendment) — "task and status
|
||||
visualization bubbles up the hierarchy as aggregation over workspaces
|
||||
the reader is authorized on" (§8.1.3); roll-up is never a write and
|
||||
bubble-up views are generated projections, non-authoritative and never
|
||||
import sources (§8.2.2); the express, narrow carve-out from the
|
||||
portfolio-analytics non-goal covers per-workspace task counts and
|
||||
statuses aggregated up the parent chain over readable workspaces, and
|
||||
nothing beyond that boundary (§8.2.4); acceptance requires that roll-up
|
||||
endpoints cannot mutate state and that a reader sees aggregates only
|
||||
over workspaces they are authorized on, with no cross-tenant existence
|
||||
oracles (§8.3). A5 rank 5 names the deliverable: an authorized
|
||||
read-only roll-up query over only readable workspaces, at every
|
||||
hierarchy level, as its own non-mutating query tool, dependent on ranks
|
||||
1–3.
|
||||
|
||||
Revision 2 (terra review F1–F7): membership-only readability is now
|
||||
workspace-local — it contributes at the workspace node only and never
|
||||
promotes ancestor visibility; upward aggregation requires an effective
|
||||
chain role, and §1 defines direct vs effective grants in contract 2's
|
||||
terms (F1). The no-oracle rule gains a defined equivalence predicate
|
||||
(normalized byte equality with an enumerated volatile-field set) and a
|
||||
partial-scope hidden-sibling witness (F2). §2.5 enumerates the closed
|
||||
semantic result and denial schemas field-by-field, including the
|
||||
explicit-zero representation (F3). Cache invalidation, when a cache
|
||||
exists, is witnessed per invalidator class (F4). Non-authoritative and
|
||||
never-gate rules gain an import-graph/data-flow witness, and the
|
||||
mutation check is aligned to contract 1 §6.7's both-table zero-write
|
||||
assertion (F5). The fixture gains a second estate with distinct counts
|
||||
and explicit company-, estate-, project-grant, and membership cases
|
||||
(F6). The §5.3 legacy-row exclusion and pre-rank no-obligation rules
|
||||
are disclosed as drafting additions (F7).
|
||||
|
||||
Revision 3 (terra re-review residuals): the partial-scope witnesses are
|
||||
reconstructed at levels where chain grants can actually differ —
|
||||
platform-project siblings under one estate and estate siblings under
|
||||
one company — because contract 1 §3.1/§3.4 defines no workspace-level
|
||||
grant target, so no reader can hold a chain grant on two of three
|
||||
sibling workspaces (F2). §2.5 now defines one field-exact recursive
|
||||
record — every node, including the queried node and every leaf, is the
|
||||
same five-field shape with a required, deterministically ordered
|
||||
`children` array that is empty at workspaces — and the whole-result
|
||||
rules (no optional fields, denial envelope, wire faithfulness) are
|
||||
their own §2.6 at section scope (F3). The fixture assigns workspaces
|
||||
to named platform-projects, and §6.1's grant-level cases are the three
|
||||
levels contract 1 defines, with workspace-level access covered by the
|
||||
membership case and stated as having no direct chain grant (F6).
|
||||
|
||||
This contract binds the projection semantics (§2), reader authorization
|
||||
semantics (§3), read-only enforcement (§4), dependencies and phase
|
||||
timing (§5), witnesses (§6), and disclosed drafting additions (§7). It
|
||||
defines the roll-up only: hierarchy shape stays with contract 1
|
||||
(`hierarchy-schema.md`), grant vocabulary and evaluation with contract 2
|
||||
(`rbac-grant-model.md`), the task lifecycle and status taxonomy with
|
||||
`native-kanban-sot.md`'s typed surface, and the tool↔Gateway mapping
|
||||
row with contract 5 (`tool-gateway-mapping.md`).
|
||||
|
||||
## 1. Definitions
|
||||
|
||||
1. **Roll-up**: the read-only projection of per-workspace task counts
|
||||
by status, aggregated up the contract 1 parent chain (workspace →
|
||||
platform-project → estate → company).
|
||||
2. **Effective chain role** (at a node, for a reader): the role
|
||||
contract 2 §3 evaluation yields at that node — from a grant on the
|
||||
node itself (a **direct grant**) or from a grant on an ancestor
|
||||
whose domain covers it (an **inherited grant**, contract 2 §3.2).
|
||||
The role vocabulary is contract 2 §2's; this contract adds no role
|
||||
and no new authority source.
|
||||
3. **Chain-readable workspace** (for a reader): a workspace where the
|
||||
reader's effective chain role permits reading task state.
|
||||
4. **Member-readable workspace** (for a reader): a workspace readable
|
||||
only through workspace membership under the SOT's own membership
|
||||
rules (REQ-ID-001), with no effective chain role. Membership
|
||||
confers workspace-local semantics only (contract 2 §3.1, §7.4): it
|
||||
never contributes authority, visibility, or aggregation upward.
|
||||
5. **Aggregation scope** (of a hierarchy node, for a reader): the set
|
||||
of chain-readable workspaces in that node's descendant subtree;
|
||||
plus, when the node is itself a workspace, that workspace if it is
|
||||
chain-readable or member-readable. A member-readable workspace
|
||||
therefore contributes to exactly one node's aggregation scope: its
|
||||
own.
|
||||
6. **Projection**: a generated, non-authoritative view in the sense of
|
||||
`native-kanban-sot.md` §3 invariant 5 — derived from SOT rows,
|
||||
never an import source, never authoritative.
|
||||
|
||||
## 2. Projection semantics
|
||||
|
||||
1. **Aggregate content.** The roll-up for a node reports, per
|
||||
workspace in the reader's aggregation scope and as subtree totals:
|
||||
task counts keyed by the typed lifecycle's status values (owned by
|
||||
`native-kanban-sot.md`; this contract introduces no status), and
|
||||
nothing else. Direct count/status aggregation is the entire
|
||||
surface.
|
||||
2. **Every level.** The roll-up is queryable at workspace,
|
||||
platform-project, estate, and company level. A node's totals equal
|
||||
the sum over its aggregation scope; chain resolution is contract 1
|
||||
§2.5's (every workspace resolves to exactly one chain), so no
|
||||
workspace is counted twice and none is orphaned.
|
||||
3. **Carve-out boundary.** Everything beyond direct count/status
|
||||
aggregation — metrics, trends, forecasting, scoring, velocity,
|
||||
cross-workspace derived analytics, dashboards computed across
|
||||
workspaces — remains a `native-kanban-sot.md` §6 non-goal
|
||||
(§8.2.4). The response schema is closed (§2.5; §6.7 witness):
|
||||
adding any field is an amendment to this contract.
|
||||
4. **Non-authoritative.** No consumer may treat roll-up output as a
|
||||
source of record; it is recomputable at any time from SOT rows and
|
||||
is never imported, persisted as authoritative state, or used to
|
||||
gate or deny work (witness §6.8 — both the write-path and the
|
||||
decision-path prohibitions are witnessed).
|
||||
5. **Closed semantic schema.** The successful result is exactly one
|
||||
**roll-up node record**, a single recursive shape used at every
|
||||
depth. A roll-up node record consists of exactly these five
|
||||
fields, and no others:
|
||||
- `id`: the node's identifier.
|
||||
- `type`: one of the four contract 1 levels.
|
||||
- `name`: the node's name.
|
||||
- `totals`: one entry per status value of the typed lifecycle —
|
||||
every status key present, a count of zero represented explicitly
|
||||
as `0`, never by key absence. At a workspace node, `totals` is
|
||||
that workspace's own counts; at any other node, `totals` is the
|
||||
sum over the node's aggregation scope (§2.2). This is how §2.1's
|
||||
"per workspace and as subtree totals" content is carried:
|
||||
per-workspace counts are the leaf records' `totals`, subtree
|
||||
totals are the interior records' `totals`.
|
||||
- `children`: a required array, present on EVERY node record. Its
|
||||
elements are the reader-visible (§3.2) child nodes of this node,
|
||||
each itself a complete roll-up node record, recursing down to
|
||||
the workspaces in the reader's aggregation scope. At a workspace
|
||||
node the array is exactly `[]` — a workspace record never has
|
||||
children. The array is ordered deterministically, ascending by
|
||||
`id`; the implementing PR asserts that ordering. A node outside
|
||||
§3.2 visibility never appears at any depth.
|
||||
|
||||
The queried node's record IS the whole result — there is no
|
||||
wrapper field around it.
|
||||
|
||||
6. **Whole-result rules.** There are no optional result fields at any
|
||||
depth. The denial/nonexistent response is the contract 5 §4.2
|
||||
not-found-class error envelope with no fields beyond that
|
||||
envelope. The wire DTO is expressed under contract 5 §4.1, and
|
||||
MUST be a faithful serialization of exactly the §2.5 recursive
|
||||
record: a wire field with no corresponding semantic field is a
|
||||
conformance defect.
|
||||
|
||||
## 3. Reader authorization semantics
|
||||
|
||||
1. **Scope rule.** A reader's roll-up over any node aggregates ONLY
|
||||
the reader's aggregation scope (§1.5). An unreadable workspace
|
||||
contributes nothing to any total — not a count, not a row, not a
|
||||
presence marker. A member-readable workspace contributes only at
|
||||
the workspace node itself (§1.4–§1.5): querying it directly
|
||||
succeeds; it never appears in, and never adds to, any ancestor's
|
||||
response for that reader.
|
||||
2. **Node visibility.** A node appears in a roll-up response iff the
|
||||
reader's aggregation scope at that node is non-empty, or the
|
||||
reader holds an effective chain role at the node (§1.2 — direct or
|
||||
inherited; contract 2 §3.2 makes a grant's domain the node and its
|
||||
subtree, so an ancestor grant makes empty descendants visible per
|
||||
the ruling). Per the ruling below, a node with an effective chain
|
||||
role but an empty aggregation scope appears with zero counts.
|
||||
Workspace membership alone never makes any non-workspace node
|
||||
visible. A node where the reader has neither an effective chain
|
||||
role nor a non-empty aggregation scope does not appear at all.
|
||||
3. **No existence oracle.** The response MUST NOT disclose the
|
||||
existence, count, name, or any property of unreadable workspaces
|
||||
or of nodes outside §3.2 visibility — no "N workspaces hidden"
|
||||
fields, no total-vs-visible discrepancy fields. A query naming a
|
||||
node outside §3.2 visibility MUST satisfy the §3.4 response
|
||||
equivalence with a query naming a nonexistent node (fail closed,
|
||||
`rbac-grant-model.md` §3.5 pattern: a decision path that cannot
|
||||
read grant state denies).
|
||||
4. **Response equivalence predicate.** Two responses are equivalent
|
||||
when they carry the identical HTTP status, the identical contract
|
||||
5 §4.2 error code, and byte-identical bodies after normalizing
|
||||
exactly the declared volatile envelope fields — correlation id and
|
||||
response timestamp, and nothing else. The implementing PR declares
|
||||
that volatile-field list in the witness; any additional
|
||||
normalization is a conformance defect. This is contract 5 §4.2's
|
||||
same code/status/shape rule made executable.
|
||||
5. **Live evaluation.** Readability is evaluated per contract 2 §3.5
|
||||
(live rows or transactionally-invalidated cache). Revocation
|
||||
propagates per contract 2 §6: the next roll-up query decided after
|
||||
the revoking transaction commits excludes the revoked scope.
|
||||
|
||||
## 4. Read-only enforcement
|
||||
|
||||
1. **Never a write.** No roll-up path may mutate, claim, order, or
|
||||
gate work in any workspace (§8.2.2). The roll-up ships as a
|
||||
non-mutating query tool (A5 rank 5) — a query surface with no
|
||||
command counterpart.
|
||||
2. **Mechanical enforcement.** The implementing PR executes roll-up
|
||||
database work inside read-only transactions (or an equivalently
|
||||
privilege-restricted path), so a mutation attempt fails at the
|
||||
database boundary, not only by convention.
|
||||
3. **Freshness.** v1 computes the roll-up live from SOT rows at query
|
||||
time. A cache is an implementation option only if it is
|
||||
invalidated in the same transaction as any task, hierarchy, grant,
|
||||
or membership mutation that affects it (each invalidator class
|
||||
witnessed, §6.5), and it is never authoritative (§1.6).
|
||||
|
||||
## 5. Dependencies and phase timing
|
||||
|
||||
1. The roll-up depends on A5 ranks 1–3: contract 1's hierarchy tables
|
||||
(the parent chain), contract 2's evaluator (readability), and the
|
||||
typed Kanban lifecycle (the task state being counted). It ships
|
||||
after them and reads their surfaces; it defines none of them.
|
||||
2. The roll-up query is one tool with one Gateway mapping row under
|
||||
contract 5's regime (request/result/error/audit contracts there);
|
||||
this contract binds its semantics (§2.5 defines the semantic
|
||||
fields the contract 5 §4.1 DTO serializes), not its wire encoding.
|
||||
3. Legacy task rows outside the typed lifecycle are not aggregated;
|
||||
the roll-up begins counting a workspace's tasks when they exist in
|
||||
the typed surface. No roll-up obligation attaches to v1 before
|
||||
ranks 1–3 exist. Both rules are drafting additions disclosed in §7
|
||||
(they trace to no §8 sentence).
|
||||
|
||||
## 6. Verification requirements
|
||||
|
||||
Binding on the implementing PRs. Every witness names, in its
|
||||
implementation, the exact endpoints/tools, tables, and fixtures it
|
||||
exercises. The base fixture seeds two companies; under company A **two
|
||||
estates with distinct, non-identical count profiles**: estate A1 with
|
||||
two platform-projects — P1 holding workspaces W1 and W2, P2 holding
|
||||
workspace W3 — and estate A2 with one platform-project P3 holding one
|
||||
workspace W4, all with known task counts across at least three
|
||||
statuses; under company B one workspace.
|
||||
|
||||
1. **Correctness witnesses:** for a reader holding a direct company-A
|
||||
grant, roll-up totals at every level equal the seeded sums — each
|
||||
workspace, each platform-project, estate A1 and estate A2
|
||||
separately (their distinct profiles asserted distinct), and the
|
||||
company total equal to A1+A2 — keyed by the typed status values,
|
||||
with no double count across the chain. For a reader holding a
|
||||
direct estate-A1 grant, the estate-A1 result equals the A1 sum and
|
||||
a company-A query returns company A with exactly A1's contribution
|
||||
(estate A2 invisible). Each of the three chain grant levels
|
||||
contract 1 §3.1 defines — company, estate, platform-project
|
||||
(below, §6.2) — has an explicit direct-grant case, none simulated
|
||||
by unioning lower access. Workspace-level access has NO direct
|
||||
chain grant (contract 1 §3.1/§3.4 define no workspace grant
|
||||
target) and is covered by the §6.2 membership case.
|
||||
2. **Scope witnesses:** a reader with a direct grant on
|
||||
platform-project P1 only sees exactly P1's subtree counts
|
||||
(W1+W2): a P1 query returns W1+W2; an estate-A1 query returns the
|
||||
estate node with exactly P1's contribution, sibling project P2 and
|
||||
its workspace W3 absent at every depth; a company-A query likewise
|
||||
carries only P1's contribution. An estate-sibling case: a reader
|
||||
with a direct grant on estate A1 only queries company A and
|
||||
receives exactly A1's contribution, estate A2 absent. (Chain
|
||||
grants exist only at company, estate, and platform-project —
|
||||
contract 1 §3.1 — so partial scope among SIBLING WORKSPACES of
|
||||
one project is not constructible by grants and is not witnessed;
|
||||
the constructible partial-scope cases are the project- and
|
||||
estate-sibling ones above.) **Membership locality (§1.4):** a member-only reader queries
|
||||
the workspace directly and receives its counts; the same reader
|
||||
querying the workspace's parent (or any ancestor) receives the
|
||||
§3.4-equivalent nonexistent-node response, and no ancestor
|
||||
response for any other reader changes because of that membership.
|
||||
3. **No-oracle witnesses:** the P1-only reader's estate-A1 response
|
||||
above contains no field disclosing P2's or W3's existence
|
||||
(closed-schema comparison against an estate-A1-granted reader's
|
||||
response: identical field set, differing only in counts and
|
||||
visible nodes). **Partial-scope hidden node:** the P1-only reader
|
||||
— who sees estate A1 and the P1 subtree — queries hidden sibling
|
||||
project P2 by its real id, and separately hidden workspace W3 by
|
||||
its real id; each response satisfies the §3.4 equivalence
|
||||
predicate against the same query naming a nonexistent id, under
|
||||
one fixed request context with the declared volatile-field
|
||||
normalization. **Cross-tenant:** an unauthorized reader naming company B receives
|
||||
a response §3.4-equivalent to naming a nonexistent id. Each
|
||||
equivalence check is executable byte comparison after the declared
|
||||
normalization, not a shape judgment.
|
||||
4. **Empty-vs-hidden witness (ruling):** a reader granted (direct
|
||||
chain grant) on an empty platform-project receives it with zero
|
||||
counts — every status key present at `0` (§2.5); with the grant
|
||||
deleted, the same query returns the §3.4-equivalent
|
||||
nonexistent-node response. An inherited-grant case: a company
|
||||
grant makes an empty descendant platform-project visible with zero
|
||||
counts.
|
||||
5. **Cache-invalidation witnesses (conditional):** bound only if the
|
||||
implementation caches — for EACH invalidator class, prime the
|
||||
cache, commit one mutation of that class, and assert the next
|
||||
query reflects it: a task status change, a task creation, a
|
||||
membership removal (the member-readable workspace disappears from
|
||||
its own node's next query), a workspace reparenting (both old and
|
||||
new parent totals correct), and a grant revocation. A live
|
||||
(cacheless) v1 implementation records that fact and the witnesses
|
||||
bind at the PR that introduces a cache.
|
||||
6. **Mutation witnesses:** the roll-up surface rejects every mutating
|
||||
verb/command; a crafted attempt to issue a write through the
|
||||
roll-up's database path fails at the read-only boundary (§4.2);
|
||||
after any roll-up query, the row diff is empty across BOTH the
|
||||
workspace tables and the hierarchy tables (contract 1 §6.7's
|
||||
both-table zero-write assertion).
|
||||
7. **Closed-schema witness:** the response is asserted field-exact
|
||||
against the §2.5 recursive record at every depth — exactly
|
||||
`id`/`type`/`name`/`totals`/`children` on every node, every typed
|
||||
status present with explicit zeros, `children: []` at every
|
||||
workspace record, the declared ascending-`id` ordering, no
|
||||
wrapper field — and a response carrying any field outside the
|
||||
record at any depth fails the assertion (carve-out boundary,
|
||||
§2.3). The denial envelope is asserted field-exact against
|
||||
contract 5 §4.2's envelope (§2.6).
|
||||
8. **Non-authoritative and never-gate witnesses:** (a) a static
|
||||
production import-graph inventory (hierarchy contract §6.3 style,
|
||||
production code over `apps/` and `packages/`, tests excluded)
|
||||
shows no production module imports the roll-up query module or its
|
||||
result DTO into any SOT write path, any authorization/gating
|
||||
decision path, or any persistence beyond the response lifetime —
|
||||
asserted in both directions (the roll-up module's consumers are
|
||||
enumerated and each is a presentation surface); (b) a behavioral
|
||||
probe: with roll-up output artificially perturbed (test double),
|
||||
no authorization outcome and no work-gating decision anywhere in
|
||||
the fixture suite changes — proving no gate consumes it.
|
||||
9. **Revocation witness:** after revoking the grant that made a
|
||||
subtree readable, the next roll-up query excludes it (contract 2
|
||||
§6.2 bound).
|
||||
|
||||
## 7. Drafting additions (PRD §12.1 disclosure)
|
||||
|
||||
Proposed drafting additions, visible here for ratification, each
|
||||
severable; the aggregation itself, its authorization scope, its
|
||||
read-only nature, and the no-oracle acceptance are traced to
|
||||
`native-kanban-sot.md` §8 and are not additions:
|
||||
|
||||
1. The §3.2 node-visibility rule and the granted-but-empty behavior
|
||||
(the ruling below).
|
||||
2. The §3.3–§3.4 nonexistent-node response equivalence, with its
|
||||
normalized-byte-equality predicate, as the concrete no-oracle
|
||||
mechanism.
|
||||
3. The §4.2 read-only-transaction mechanical enforcement.
|
||||
4. The §4.3 cache option with transactional invalidation and the
|
||||
§6.5 per-invalidator witnesses.
|
||||
5. The §2.5 closed response schema as an amendment boundary.
|
||||
6. The §1.4 membership-locality rule — membership-only readability
|
||||
contributes at the workspace node only (this contract's
|
||||
reconciliation of `native-kanban-sot.md` §8.1.3 "authorized on"
|
||||
with contract 2 §3.1/§7.4's workspace-local membership).
|
||||
7. The §5.3 legacy-row exclusion and the §5.3 pre-rank no-obligation
|
||||
rule.
|
||||
|
||||
## Ruling request
|
||||
|
||||
Ruling requested (one decision): shall a node the reader holds an
|
||||
effective chain role on (direct or inherited, §1.2) but whose
|
||||
aggregation scope is empty appear in the roll-up with zero counts
|
||||
(recommended — it lets the UI show a granted-but-empty subtree
|
||||
honestly) — or, as the alternative, be indistinguishable from a
|
||||
nonexistent node until it contains a readable workspace?
|
||||
Reference in New Issue
Block a user