Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9287467
feat: add cockpit C1 authority boundary baseline
LogicDuke Aug 15, 2026
7575192
fix: harden cockpit operation resolution
LogicDuke Aug 15, 2026
a3576f0
Merge pull request #16 from LogicDuke/repair/c1-a01-map-intrinsic
LogicDuke Aug 15, 2026
96f2f0f
fix: clarify cockpit merge authority guarantees
LogicDuke Aug 15, 2026
ffad93c
fix: clarify merge target freshness boundary
LogicDuke Aug 16, 2026
146734b
Merge pull request #17 from LogicDuke/repair/c1-a02-merge-authority-c…
LogicDuke Aug 16, 2026
14b4fb3
fix: reject inherited repair list elements
LogicDuke Aug 16, 2026
cc14f2b
Merge pull request #19 from LogicDuke/repair/c1-a03-own-list-elements
LogicDuke Aug 16, 2026
52f1a79
fix: reject Git-equivalent repair refs
LogicDuke Aug 16, 2026
c743b65
docs: clarify symbolic ref resolution boundary
LogicDuke Aug 19, 2026
fc49e9a
docs: complete execution boundary ref contract
LogicDuke Aug 19, 2026
afba343
docs: make protected parent role-aware
LogicDuke Aug 20, 2026
c8fde78
Merge pull request #37 from LogicDuke/repair/c1-a04-change-request-ta…
LogicDuke Aug 20, 2026
bf78cf6
docs: bind change requests at provider boundary
LogicDuke Aug 20, 2026
7810d6e
docs: allow identity-preserving provider resolution
LogicDuke Aug 20, 2026
0bef03c
Merge pull request #39 from LogicDuke/repair/c1-provider-resolution-i…
LogicDuke Aug 20, 2026
838d8ed
Merge pull request #38 from LogicDuke/repair/c1-change-request-provid…
LogicDuke Aug 20, 2026
077933d
C1: bind repair.push source to authorized ref
LogicDuke Aug 20, 2026
6812607
C1: distinguish repair.push source role
LogicDuke Aug 20, 2026
1fa4cf6
Merge pull request #41 from LogicDuke/repair/pr040-push-source-role-w…
LogicDuke Aug 20, 2026
6fa3e95
Merge pull request #40 from LogicDuke/repair/c1-push-source-binding
LogicDuke Aug 20, 2026
7444080
Merge pull request #36 from LogicDuke/repair/c1-a04-exec-boundary-con…
LogicDuke Aug 20, 2026
467ccc3
C1-A04 clarify effective repository ref identity
LogicDuke Aug 21, 2026
7a3e53b
Merge pull request #42 from LogicDuke/repair/c1-a04-repository-ref-id…
LogicDuke Aug 21, 2026
a466075
Merge pull request #35 from LogicDuke/repair/c1-a04-symref-claim
LogicDuke Aug 21, 2026
dd1bdd4
Merge pull request #21 from LogicDuke/repair/c1-a04-ref-alias
LogicDuke Aug 21, 2026
f8eb259
fix(c1): snapshot trusted state before hostile reads
LogicDuke Aug 21, 2026
aa0806a
Merge pull request #43 from LogicDuke/repair/c1-snapshot-trusted-befo…
LogicDuke Aug 21, 2026
3c79224
Merge pull request #14 from LogicDuke/cockpit/c1-job-authority
LogicDuke Aug 21, 2026
04f751e
Cockpit D1 — add read-only Cockpit read-model contract
LogicDuke Aug 21, 2026
72ee36e
Cockpit D1 — reject unreadable optional fields
LogicDuke Aug 21, 2026
bdb16a4
Merge pull request #45 from LogicDuke/repair/d1-44-optional-getter
LogicDuke Aug 21, 2026
0a055e3
Cockpit D1 — isolate snapshot graph from prototype poisoning
LogicDuke Aug 21, 2026
cd82ee9
D1-46-F1 isolate freezeList descriptor from prototype poisoning
LogicDuke Aug 21, 2026
0f70d29
Merge pull request #46 from LogicDuke/repair/d1-44-json-prototype-iso…
LogicDuke Aug 22, 2026
53fdc66
D1: isolate Cockpit append descriptors
LogicDuke Aug 22, 2026
06b8230
Merge pull request #47 from LogicDuke/repair/d1-44-append-descriptor-…
LogicDuke Aug 22, 2026
2921504
Merge pull request #44 from LogicDuke/cockpit/d1-read-model-contract
LogicDuke Aug 22, 2026
df1d3df
fix: isolate evidence freshness append descriptors
LogicDuke Aug 22, 2026
e1b1a2e
Merge pull request #48 from LogicDuke/repair/evidence-freshness-descr…
LogicDuke Aug 22, 2026
140e622
feat: add Cockpit evidence freshness projection
LogicDuke Aug 22, 2026
8cfe1f1
Merge pull request #49 from LogicDuke/cockpit/d2-evidence-freshness-p…
LogicDuke Aug 23, 2026
9e2d4b2
fix: isolate repair-job append descriptors
LogicDuke Aug 23, 2026
50bb632
Merge pull request #50 from LogicDuke/repair/c1-repair-job-descriptor…
LogicDuke Aug 23, 2026
5f02164
fix: insulate domain append descriptors
LogicDuke Aug 23, 2026
556144b
Merge pull request #51 from LogicDuke/repair/domain-append-descriptor…
LogicDuke Aug 24, 2026
5accbca
Merge main 556144b into PR 007 reconciliation
LogicDuke Aug 24, 2026
15a35cc
fix: fail closed on unreadable push force flag
LogicDuke Aug 24, 2026
89ca7ba
Merge pull request #54 from LogicDuke/repair/pr053-job-operation-forc…
LogicDuke Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
834 changes: 834 additions & 0 deletions docs/architecture/C1-repair-job-authority.md

Large diffs are not rendered by default.

106 changes: 106 additions & 0 deletions docs/architecture/D1-cockpit-read-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Cockpit Snapshot / Read-Model Contract (Cockpit D1)

Status: V1 defaults. Superseded only by an explicit architecture decision.

## Scope

D1 adds the pure TypeScript contract for future read-only Cockpit data:

collector observation -> CockpitSnapshot -> read-only presentation

**Contract only. Nothing is collected, persisted, served, or executed.** D1
contains no filesystem access, no Evidence Store implementation, no collectors,
no Git or GitHub access, no subprocess, no HTTP/REST/WebSocket/SSE, no
frontend, and no Autoflow integration. Every value in a snapshot — including
the observation timestamp — is caller-supplied data.

## Authority model

**The Cockpit is presentation and observability, never authority.**

AgentBridge V1 remains read-only against managed repositories, and D1 grants
nothing. A snapshot is a *derived echo* of domain truth for display. No field
in the envelope is typed to carry a decision, permit, approval, or
authorization, so an authority-shaped value has nowhere to land, and the
reader's accepted output is a frozen copy that carries no stray input fields.

Domain/evidence truth and the Cockpit view model are distinct by construction:

- The frozen kernel layers (PR 002–006, C1) remain the only sources of domain
truth and are imported, never re-declared.
- Any future durable snapshot storage belongs to the Evidence Store boundary.
D1 defines only the serializable envelope such storage would carry.

## Modules

| Module | Responsibility |
| --- | --- |
| `src/cockpit/read-model.ts` | Snapshot envelope, provenance, read models, fail-closed reader |
| `src/cockpit/index.ts` | Public re-exports of the D1 contract |

## The envelope

One `CockpitSnapshot` describes exactly one repository at one observed HEAD:

- `repository` — repository identity, observed HEAD SHA, optional canonical
default-branch ref (C1's `refs/heads/<name>` spelling).
- `provenance` — collector/source identity and the externally supplied
observation timestamp. Audit metadata, inert as authority.
- `pullRequests`, `evidence`, `findings`, `repairJobs` — bounded, all-or-nothing
lists of frozen read models.

Every accepted field is a primitive, `null`, or a frozen array of frozen
records, so a snapshot survives a plain-JSON round trip unchanged.

## Reused domain vocabulary (never duplicated)

| Reused | From |
| --- | --- |
| `EvidenceKind`, `EvidenceSource` + guards | PR 004 `evidence.ts` |
| `FreshnessState` (`CURRENT`/`STALE`/`INVALID`) | PR 004 `evidence-freshness.ts` |
| `ReviewSeverity`, `ReviewClassification`, `ReviewFindingStatus` + readers, `REVIEW_BOUNDS`, `readText` | PR 005 `review.ts` |
| `readExactIdentifier`, `readOwnProperty`, `readCanonicalBranchRef`, `append`, `containsValue` | C1 `repair-job.ts` |
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Freshness versus disposition

The formal finding freshness vocabulary is PR 004's and is not extended. A
finding read model may carry `advisoryFreshness` — a *recomputable echo* of a
freshness evaluation — but it is never authority: the envelope carries the
finding's `reviewedCommitSha` and the repository's `observedHeadSha`, so a
consumer that needs the truth recomputes with the domain kernel. An
unrecognised advisory value folds to `null` ("no claim"), never to a state.

Presentation triage categories (`maintenance-observation`,
`future-layer-obligation`, `optional-cleanup`, `deferred`, `unspecified`) are a
separate `CockpitFindingDisposition` axis. A disposition is not a finding
classification, not a freshness state, and adds no member to any domain
vocabulary; the two axes share no member and a value from one folds fail-closed
in the other.

## Hostile-data discipline

A snapshot is re-read from JSON-shaped, unknown-provenance data, so
`readCockpitSnapshot` follows the boundary discipline already established in
PR 004–006 and C1:

- intrinsics captured at module load; imported domain readers capture their own
- own-properties only — inherited and `__proto__`-planted values never become
fields
- every value read exactly once into a local; guarded reads that fail closed on
throwing getters, Proxy traps, and revoked Proxies
- identity-shaped fields exact-or-rejected (never trimmed or truncated);
descriptive vocabulary folded to its fail-closed member; prose bounded
- bounded, all-or-nothing lists — sparse holes, inherited elements, lying
lengths, and oversize reject the whole snapshot
- deterministic invalid-field reporting in `COCKPIT_SNAPSHOT_FIELD_ORDER`
- the accepted snapshot is a deep-frozen copy built from validated locals,
never the caller's objects

## Tests

`tests/cockpit/` covers valid construction, deterministic rejection,
inherited-property refusal, unstable-getter single-read discipline, snapshot
immutability, JSON round-trip stability, domain-vocabulary reuse,
freshness/disposition separation, and a bounded source-purity invariant that
proves `src/cockpit/` references no filesystem, subprocess, network, process
execution, or Git/GitHub operation and imports only the domain kernel.
154 changes: 154 additions & 0 deletions docs/architecture/D2-cockpit-evidence-freshness-projection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Cockpit Evidence Freshness Projection (Cockpit D2)

Status: V1 defaults. Superseded only by an explicit architecture decision.

## Scope

D2 adds exactly one responsibility to the Cockpit layer: project PR 004's
evidence-freshness answers for the evidence records contained in one
**already-validated** `CockpitSnapshot`.

validated CockpitSnapshot
-> snapshot evidence read models
-> minimal EvidenceRecord reconstruction
-> EvidenceTarget derived from the enclosing snapshot
-> PR 004 evaluateEvidenceSet()
-> immutable Cockpit presentation projection

There is no reverse arrow. D2 is presentation and observability only.

D2 is **not**: hostile JSON validation, evidence authority, policy, merge
readiness, reviewer quorum, execution authority, repair authority, a collector,
persistence, or any Git/GitHub, filesystem, network, or subprocess I/O.

## Trust boundary (Option A)

D2 accepts only an already-valid D1 `CockpitSnapshot`.

- **D1 owns hostile `unknown` input.** JSON-shaped, unknown-provenance data goes
through `readCockpitSnapshot()`, which validates or rejects it.
- **D2 owns the projection of a valid snapshot.** Its public API takes a
`CockpitSnapshot`, never `unknown`.
- **D2 does not duplicate `readCockpitSnapshot`.** It adds no second
`invalidFields` envelope, no malformed-snapshot handling, no null/primitive
input semantics, no non-array evidence semantics, and no throwing-getter
validation. Those belong to D1.

Consequently a zero-result projection means exactly one thing: the valid
snapshot contains zero evidence records. Malformed input never projects as a
legitimate empty evidence set, because malformed input never reaches D2.

For a contract-valid snapshot, `projectCockpitEvidenceFreshness` is pure,
deterministic, synchronous, non-mutating, side-effect free, and returns a
deeply immutable value. Behaviour for values forced through an unsafe TypeScript
cast is intentionally undefined — that is separation of responsibilities, not a
missing defence, and no validation branch is added to support it.

## Freshness authority

PR 004 (`src/domain/evidence-freshness.ts`) is the freshness authority. D2 only
projects freshness:

- it never compares SHAs and never decides `CURRENT` / `STALE` / `INVALID`;
- it copies PR 004's `state`, `reason`, and `invalidFields` verbatim;
- it reuses `EvidenceRecord`, `EvidenceKind`, `EvidenceSource` from
`evidence.ts` and `EvidenceTarget`, `evaluateEvidenceSet`, `FreshnessState`,
`FreshnessReason`, and `FRESHNESS` from `evidence-freshness.ts` (the
`FRESHNESS_REASON` vocabulary reaches the projection verbatim through the
kernel's answers), and re-declares none of them.

## Evidence and target reconstruction

For every `CockpitEvidenceReadModel` the minimum `EvidenceRecord` is rebuilt:

{ evidenceId, repositoryId, commitSha, kind, source, reference, observedAt }

**`repositoryId` is injected from the enclosing snapshot**
(`snapshot.repository.repositoryId`). A D1 snapshot describes exactly one
repository, so per-element repository fields are neither present nor added to
the D1 read model. No metadata is attached.

Exactly one `EvidenceTarget` is built from snapshot identity:

{ repositoryId: snapshot.repository.repositoryId,
currentHeadSha: snapshot.repository.observedHeadSha }

**`observedHeadSha` is the only target HEAD.** Both identity values are read
once into locals and every record is evaluated against the same target. HEAD is
never inferred from an evidence `commitSha`, an `advisoryFreshness` echo,
finding data, a pull-request observation, reviewer output, or metadata.

## Finding freshness is out of scope

D2 neither reads nor recomputes `snapshot.findings[*].advisoryFreshness`, and
fabricates no evidence provenance from findings. D2 is evidence-record
freshness projection only; finding freshness remains a separate concern.

## Output

projectCockpitEvidenceFreshness(snapshot: CockpitSnapshot)
: CockpitEvidenceFreshnessProjection

| Type | Fields |
| --- | --- |
| `CockpitEvidenceFreshnessItem` | `evidenceId`, `kind`, `source`, `commitSha`, `state`, `reason`, `invalidFields` |
| `CockpitEvidenceFreshnessCounts` | `current`, `stale`, `invalid`, `total` |
| `CockpitEvidenceFreshnessProjection` | `repositoryId`, `observedHeadSha`, `results`, `counts` |

- `results[i]` corresponds to `snapshot.evidence[i]`: input order preserved,
nothing sorted, deduplicated, filtered, or dropped.
- `counts.total === results.length` and
`counts.current + counts.stale + counts.invalid === counts.total`.
- No `current[]` / `stale[]` / `invalid[]` buckets: they would duplicate
derivable presentation data.
- **`INVALID` is part of the domain vocabulary** and `counts.invalid` keeps the
projection structurally faithful to PR 004, **but it is not expected from a
valid D1 snapshot under the current schema**: D1 guarantees non-null identity
and structurally valid evidence, and repository identity is injected from the
same snapshot, so `REPOSITORY_MISMATCH`, `EVALUATION_TARGET_INVALID`, and
`EVIDENCE_MALFORMED` are unreachable through contractual D2 input. D2 still
copies whatever PR 004 returns without reinterpretation.

## Authority model

The projection is immutable presentation state. It carries no decision,
permit, approval, authority, merge-readiness, quorum, or repair field, and the
Cockpit architecture invariant admits exactly one non-`read*` public function —
`projectCockpitEvidenceFreshness` — without granting a general `project*`
namespace. No collector, persistence, or I/O is introduced.

## Bounds and immutability

- **No new bound beyond D1.** D2 is bounded by D1's
`COCKPIT_BOUNDS.MAX_EVIDENCE_RECORDS` (1,000) and projects every record.
- The returned projection is deeply frozen, detached from the caller's
snapshot, and contains only primitives and frozen records/lists; it survives
`JSON.parse(JSON.stringify(projection))` with its enumerable data unchanged.
- Although the input is trusted, the realm may be mutated between D1
validation and D2 projection. D2 captures the intrinsics it relies on
(`Object.freeze`, `Object.defineProperty`, `Object.setPrototypeOf`) at module
load, builds lists by own-element definition (no `push`, `map`, `filter`,
spread, or iterator), gives its descriptors a `null` prototype before
`defineProperty` consumes them, gives returned records a `null` prototype, and
shadows `toJSON` on returned lists — so a poisoned `Object.prototype` or
`Array.prototype` cannot reach the projection or its JSON form. This is realm
robustness, not input validation: no D1 field is re-validated.

## Modules

| Module | Responsibility |
| --- | --- |
| `src/cockpit/evidence-freshness-projection.ts` | D2 projection types and `projectCockpitEvidenceFreshness` |
| `src/cockpit/index.ts` | Public re-export of the D2 contract |

## Tests

`tests/cockpit/evidence-freshness-projection.test.ts` covers CURRENT / STALE
projection, exact ordering and counts, parity with a direct
`evaluateEvidenceSet()` call, repository-identity injection, `observedHeadSha`
as the only target HEAD, evidence-as-HEAD refusal, finding independence, the
empty and D1-maximum cases, the no-INVALID-from-reconstruction property, deep
immutability, input non-mutation, determinism, JSON round trip, ambient
`Object.prototype` / `Array.prototype` / intrinsic-replacement robustness, and
absence of authority-shaped keys. `tests/cockpit/architecture-invariants.test.ts`
keeps the source-purity and single-exception export rules.
Loading
Loading