-
Notifications
You must be signed in to change notification settings - Fork 0
PR9 reconciliation — integrate main 556144b into PR 007 #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
LogicDuke
merged 49 commits into
pr-007/autoflow-state-machine
from
reconcile/pr009-main-556144b
Aug 24, 2026
Merged
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 7575192
fix: harden cockpit operation resolution
LogicDuke a3576f0
Merge pull request #16 from LogicDuke/repair/c1-a01-map-intrinsic
LogicDuke 96f2f0f
fix: clarify cockpit merge authority guarantees
LogicDuke ffad93c
fix: clarify merge target freshness boundary
LogicDuke 146734b
Merge pull request #17 from LogicDuke/repair/c1-a02-merge-authority-c…
LogicDuke 14b4fb3
fix: reject inherited repair list elements
LogicDuke cc14f2b
Merge pull request #19 from LogicDuke/repair/c1-a03-own-list-elements
LogicDuke 52f1a79
fix: reject Git-equivalent repair refs
LogicDuke c743b65
docs: clarify symbolic ref resolution boundary
LogicDuke fc49e9a
docs: complete execution boundary ref contract
LogicDuke afba343
docs: make protected parent role-aware
LogicDuke c8fde78
Merge pull request #37 from LogicDuke/repair/c1-a04-change-request-ta…
LogicDuke bf78cf6
docs: bind change requests at provider boundary
LogicDuke 7810d6e
docs: allow identity-preserving provider resolution
LogicDuke 0bef03c
Merge pull request #39 from LogicDuke/repair/c1-provider-resolution-i…
LogicDuke 838d8ed
Merge pull request #38 from LogicDuke/repair/c1-change-request-provid…
LogicDuke 077933d
C1: bind repair.push source to authorized ref
LogicDuke 6812607
C1: distinguish repair.push source role
LogicDuke 1fa4cf6
Merge pull request #41 from LogicDuke/repair/pr040-push-source-role-w…
LogicDuke 6fa3e95
Merge pull request #40 from LogicDuke/repair/c1-push-source-binding
LogicDuke 7444080
Merge pull request #36 from LogicDuke/repair/c1-a04-exec-boundary-con…
LogicDuke 467ccc3
C1-A04 clarify effective repository ref identity
LogicDuke 7a3e53b
Merge pull request #42 from LogicDuke/repair/c1-a04-repository-ref-id…
LogicDuke a466075
Merge pull request #35 from LogicDuke/repair/c1-a04-symref-claim
LogicDuke dd1bdd4
Merge pull request #21 from LogicDuke/repair/c1-a04-ref-alias
LogicDuke f8eb259
fix(c1): snapshot trusted state before hostile reads
LogicDuke aa0806a
Merge pull request #43 from LogicDuke/repair/c1-snapshot-trusted-befo…
LogicDuke 3c79224
Merge pull request #14 from LogicDuke/cockpit/c1-job-authority
LogicDuke 04f751e
Cockpit D1 — add read-only Cockpit read-model contract
LogicDuke 72ee36e
Cockpit D1 — reject unreadable optional fields
LogicDuke bdb16a4
Merge pull request #45 from LogicDuke/repair/d1-44-optional-getter
LogicDuke 0a055e3
Cockpit D1 — isolate snapshot graph from prototype poisoning
LogicDuke cd82ee9
D1-46-F1 isolate freezeList descriptor from prototype poisoning
LogicDuke 0f70d29
Merge pull request #46 from LogicDuke/repair/d1-44-json-prototype-iso…
LogicDuke 53fdc66
D1: isolate Cockpit append descriptors
LogicDuke 06b8230
Merge pull request #47 from LogicDuke/repair/d1-44-append-descriptor-…
LogicDuke 2921504
Merge pull request #44 from LogicDuke/cockpit/d1-read-model-contract
LogicDuke df1d3df
fix: isolate evidence freshness append descriptors
LogicDuke e1b1a2e
Merge pull request #48 from LogicDuke/repair/evidence-freshness-descr…
LogicDuke 140e622
feat: add Cockpit evidence freshness projection
LogicDuke 8cfe1f1
Merge pull request #49 from LogicDuke/cockpit/d2-evidence-freshness-p…
LogicDuke 9e2d4b2
fix: isolate repair-job append descriptors
LogicDuke 50bb632
Merge pull request #50 from LogicDuke/repair/c1-repair-job-descriptor…
LogicDuke 5f02164
fix: insulate domain append descriptors
LogicDuke 556144b
Merge pull request #51 from LogicDuke/repair/domain-append-descriptor…
LogicDuke 5accbca
Merge main 556144b into PR 007 reconciliation
LogicDuke 15a35cc
fix: fail closed on unreadable push force flag
LogicDuke 89ca7ba
Merge pull request #54 from LogicDuke/repair/pr053-job-operation-forc…
LogicDuke File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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` | | ||
|
|
||
| ## 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
154
docs/architecture/D2-cockpit-evidence-freshness-projection.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.