[architecture] Define unified MCP operator presentation for #180 - #199
Conversation
Architecture review updateCompleted two orthogonal architecture passes for #180. Round 1 findings addressed
Architecture updates are in Round 2 resultNo further architecture findings identified in the reviewed scope. S5 remains reader/presentation-only and must not persist or infer admission policy. This is an architecture-only PR and does not implement #180. |
Integrated architecture review — round 3 findings (before correction)Verdict: Needs architecture changes. Implementation must not proceed from this revision. High — the valid tuple table is contradictory
High —
|
Integrated architecture review — round 7 findings before correctionThe fresh integrated pass found two downstream presentation blockers:
The sibling-wait state also needs the S4-owned convergence transition specified on #198; S5 must remain actionless until that durable transition makes the task exactly |
Integrated architecture review — Round 8 findings (before correction)Verdict for this round: blocked by two S5 presentation contradictions inherited from the S4 persistence model.
Required correction: remove the non-durable failed-finalizing variants; retain only live phases derivable from persisted assembly/delivery; import S4's new closed post-submission stage; add bounded recovery/integrity copy; and keep all unknown or inconsistent tuples neutral and actionless. Inspected stack head: |
Round 8 addendum — complete-tuple validationOne additional S5 consistency requirement was found before correction: a packet recovery marker does not contain enough assembly detail to validate the normative terminal tuple by itself. The current-state reader must bind the marker to the exact prior audit/artifact, prove their typed terminal tuples are equal, and validate assembly + delivery + terminal status + failure code/stage together. Missing, mismatched, or terminal-success-plus-failure-marker evidence is neutral, non-retryable, and actionless. |
Integrated architecture review — Round 9 downstream findings (before correction)S5 needs two downstream states from the fresh S4 review:
Typed packet-integrity alerts also need neutral static copy, an owner/runbook reference, no web repair CTA, and no raw path/error detail. The last closed stage is Posted before correction. |
Round 9 addendum — review-pending sibling presentationWhen any sibling package is Posted before correction. |
Integrated architecture review — Round 10 presentation dependencySeverity: Medium The current integrity UI language says an audit/artifact mismatch needs operator repair. A true mismatch may not be repairable without rewriting immutable evidence, which the architecture correctly forbids. S4 is being corrected to provide an authorized append-only S5 must present this as permanent evidence quarantine with an explicit close/abandon action for authorized Release/DevOps operators. It must not promise repair, automatic recovery, retry, or successful packet reconstruction. The live state remains evidence-derived from the integrity alert/resolution records. No other S5 presentation blocker was found in this pass. |
Integrated architecture review — Round 12 S5 findings (before correction)Medium — The authoritative current-state join omits repository evidenceS5 joins the packet audit/artifact and host ledger, then binds the current marker to host-review fingerprints. S4 also requires exact equality for the pre-submission repository baseline, post-quiescence comparison, repository-review state, and their fingerprint. Without that join, stale or mismatched marker evidence could present a retry/reapproval action after a different repository review was completed. Required correction: the authoritative presenter joins and compares the complete repository baseline/change/review evidence and marker fingerprint in addition to host ledger/review evidence. Any missing, stale, or mismatched repository component renders the neutral integrity hold with no action. Medium —
|
Integrated architecture review — Round 12 recovery-action finding (before correction)High — Definitively rejected delivery can still require local-change review, but S5 exposes no review CTA
Required correction: presentation is review-first whenever either exact host or repository review is required, independent of delivery. Expose only the typed fingerprint-bound local-change review action; after it commits, present the grant-mode/delivery action. Keep “the provider rejected the request” distinct from “local files require review,” and never imply the provider changed them. This is an architecture-contract finding, not an implementation finding. |
Integrated architecture review — Round 12 tombstone presentation finding (before correction)High — A removed project must not retain an executable action surfaceS5 correctly says “Project removed — evidence retained,” but S4 currently retains nonterminal work without an all-mode archived-project claim barrier. The presenter could therefore combine retained-history copy with a live retry/reapproval/progression action. Required correction: an archived/tombstoned project is history/evidence-only. S5 renders no task execution, retry, reapproval, review-gate, or root-management CTA for cancelled packages; only explicitly authorized evidence inspection or privileged quarantine history remains. This consumes S4's bounded project-removed cancellation state and never infers cancellation from missing paths. This is an architecture-contract finding, not an implementation finding. |
4bb2989 to
a98923c
Compare
Integrated architecture review — Round 12 correctionsCorrected in
Validation: documentation-only diff; |
Integrated architecture review — Round 13 presentation finding (before correction)S5's evidence join and exact retry/review predicates must include the versioned Git-control baseline/comparison fingerprint as well as working-tree evidence. Missing, changed, unverifiable, stale, or mismatched Git-control evidence must render the same review/blocking state as repository-local changes and must expose no path, hook, config, ref, or submodule detail. Success and retry controls remain hidden unless both working-tree and Git-control comparisons are exactly |
Integrated architecture review — Round 13 advisory finding (before correction)Low — “Both packet actions” contradicts the closed three-action mutation unionThe document says both packet actions carry immutable request identity, but the typed union has three packet mutations: retry, possible-submission acknowledgement, and local-change review. This stale count can lead an implementation or test to omit stale-request protection for the third action. Required correction: say “Every packet mutation action carries…” and name all three handlers. Each handler must reject a stale |
Integrated architecture review — Round 13 advisory finding (before correction)Low —
|
Integrated architecture review — Round 13 presentation finding (before correction)Packet-free and handoff-only local-root runs need the same crash-safe local-effect/repository review lifecycle as packet runs, but must not manufacture a packet audit, packet artifact, delivery state, or packet-retry action. S5 must read the packet-independent run-effect record and render exact local-change review/quarantine state for those runs. Packet evidence stays absent; submission acknowledgement and packet retry/reapproval CTAs stay hidden. The local-change action carries the exact run-effect record ID plus evidence fingerprint and rejects stale identity without mutation. |
a98923c to
40e5e11
Compare
Integrated architecture review — Round 13 correctionCorrected in
Validation: docs-only; |
|
Round 14 finding — Medium / blocking — contradictory local-review action ownership (PR #198 primary; #199/#200 downstream) The corrected schema makes Evidence:
Failure/cross-PR impact: implementations cannot choose a single idempotency source. A packet review can write the wrong table, while packet-free and handoff-only review cannot supply a runtime-audit ID. S5 and S6 may then disagree about replay and current-state truth. Required correction: define one generic local-effect recovery endpoint and request keyed by Required proof: packet, packet-free, and handoff-only local reviews; duplicate/concurrent/stale replay; review followed by packet acknowledgement or retry; exactly one local action row and zero issuance action rows for review. This finding was published before correction. Architecture only; no implementation or merge is authorized. |
|
Round 14 finding — Medium / blocking — S5 test contract still assigns packet identity to generic local review (PR #199 primary; #200 downstream) S5’s normative types and presenter correctly split packet identity Evidence:
Cross-PR impact: S6 can implement a manifest-bound test that contradicts S4’s packet-independent action model, and packet-free/handoff-only local review cannot satisfy the test. Required correction: split the test contract. Retry and submission acknowledgement use packet v2 identity; local review uses generic local v1 identity. Explicitly require cross-type substitution to fail closed. Required proof: component and route fixtures for packet, packet-free, and handoff-only review plus stale and cross-route identities. This finding was published before correction. Architecture only; no implementation or merge is authorized. |
|
Round 14 finding — High / blocking — sole generic review does not converge the dependent packet disposition (PR #198/#199/#200) The schema and S5 UI establish exactly one packet-independent local-review action and ledger. The S4 packet marker can nevertheless store Evidence:
Invariant: host/repository review is packet-independent, uses one generic request identity/action/ledger, and any exact dependent packet disposition converges from that committed fact without acknowledging delivery or clearing packet-owned evidence. Required correction: make the generic local-review transaction the sole review mutation and replay ledger. Under the full lock order, it must also compare-and-set any exact dependent packet marker to its stored Required proof: packet+local dual markers for every grant/delivery mode; generic exact replay; stale generic-versus-packet fingerprints; local review racing packet acknowledgement/retry/reapproval; no second review action; packet-free parity. This finding was published before correction. Architecture only; no implementation or merge is authorized. |
|
Round 14 finding — High / blocking — S5 suppresses recovery forever after exact review of detected repository changes (PR #199 primary; #198/#200 contract impact) S5 says exact review advances a changed or unverifiable working-tree/Git-control run to its stored packet disposition, but its general action gate then requires both immutable comparison results to be Evidence:
Cross-PR impact: every always-allow recovery with detected and reviewed ACP changes dead-ends in S5 despite a valid S4 disposition. One-time copy can promise reapproval while the presenter suppresses it. Required correction: action eligibility must accept either Required proof: both grant modes × all delivery states × working-tree-only, Git-control-only, and both changed/unverifiable. After exact review, pre-intent always-allow exposes retry; post-intent exposes acknowledgement then retry; one-time exposes the proper reapproval sequence. Stale/different fingerprints remain blocked. This finding was published before correction. Architecture only; no implementation or merge is authorized. |
|
Round 14 finding — High / blocking — Git evidence still omits object storage and other history-authority files (PR #198 primary; #199/#200 downstream) The bounded Git-control snapshot covers configuration, hooks, Evidence:
Failure/cross-PR impact: an unconfined ACP process can truncate or replace a pack/loose object, add an alternate/replace object, run repack or garbage collection, or inject unreachable objects while the named working-tree and Git-control evidence appears unchanged. Generic local evidence can clear, S5 can show success, and root reuse can proceed against corrupted or redirected history. Required correction: add a bounded Git-storage snapshot or broaden Git-control to every file that affects object resolution/integrity. Fingerprint opaque metadata and content under the common-directory fence; if an equivalent bounded filesystem snapshot/journal cannot prove the state, fail closed as unverifiable and disable execution/root reuse. Required proof: truncate a pack and loose object; swap a pack index; write alternates/replace/grafts/shallow state; run repack/garbage collection; add unreachable objects; repeat with linked external common directories. Every case becomes changed or unverifiable and blocks success, new work, and root management without leaking paths. This finding was published before correction. Architecture only; no implementation or merge is authorized. |
|
Round 14 finding — High / blocking — packet-free stale recovery has no terminal disposition when quiescence proves no local change (PR #198 primary; #199/#200 downstream) S4 says packet-free and handoff-only crashes create Evidence:
Failure/cross-PR impact: an unchanged packet-free crash can leave a package and root permanently held with no normal action, while different implementers may silently choose incompatible retry/failure semantics. Required correction: define a discriminated generic stale-run terminal matrix. Before quiescence, remain actionless and keep the resource fence. After exact W2/service receipt and comparisons: changed/unverifiable creates the local-review marker; unchanged/not-applicable creates no local marker and atomically reaches one explicit ordinary run/package/task disposition under the project’s existing retry policy, without inventing packet evidence or automatically rerunning. Specify task and wake behavior and exact replay/repair semantics. Required proof: packet-free and handoff-only crashes at every boundary with unchanged, changed, and unverifiable comparisons; W2/quiescence wait; ordinary terminal/retry policy for unchanged; exact local review for changed; no permanent actionless post-quiescence block; no packet audit/artifact/action in any no-packet case. This finding was published before correction. Architecture only; no implementation or merge is authorized. |
|
Round 14 finding — High / blocking — no protocol-v2 membership or replacement path exists after cutover (PR #198 primary; #199/#200 operational impact) Every process incarnation is never reused and independently authenticated. At epoch 2, claims/root writes accept only an Evidence: Required correction: specify a privileged epoch-2 membership-change command/runbook: disable relevant ingress; provision the dedicated candidate; validate host, key, generation, protocol and capability; lock epoch/instances; drain, revoke and terminate the old principal; promote a bounded audited replacement; define capacity, rollback and all-active-gone recovery. W1 death must not prevent a separately authorized fresh W2 from joining safely. Proof: rolling worker/root-writer restart; abrupt W1 death; all active workers gone; at-capacity replacement; claim/promotion/recovery races; replay by the revoked principal; bounded audit and fail-closed rollback. Published before correction. Architecture only; no merge authorized. |
|
Round 14 finding — Medium / blocking — mandated integrity CLI cannot address packet-free/handoff alerts (PR #198 primary; #199/#200 impact) Integrity alerts require generic local evidence and make packet audit optional, but the only inspect/resolve command is keyed by Evidence: Required correction: key inspect/resolve by mandatory Published before correction. Architecture only; no merge authorized. |
|
Round 14 finding — Medium / blocking — S5 has no typed packet-independent quiescence or integrity state (PR #199 primary; #198/#200 contract impact)
Evidence: Required correction: make the local presenter a closed union with generic Proof: packetless/packet local markers; active/orphaned quiescence; absent/wrong run/root/fingerprint; stale task projection; transition to exact marker/hold; exhaustive unknown state; zero packet controls. Published before correction. Architecture only; no merge authorized. |
fix: fence recovery actions and project approval CAS
fix(mcp): harden canonical task client presentation
fix(mcp): pin S5 server observation
fix(mcp): harden canonical runtime presentation
feat(mcp): render canonical task recovery state
a36715d
into
architecture/issue-179-context-packet-evidence
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2cd6e6d600
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| -- The original migration finalizes its temporary owner edge. Reopen that | ||
| -- narrowly scoped, migration-login-only edge so PostgreSQL permits replacing | ||
| -- the protected routine, then close it again below. | ||
| SELECT public.forge_begin_epic_172_s4_owner_bootstrap_v1(); |
There was a problem hiding this comment.
Route normal migrations through the S5 owner handoff
When migration 0028 is pending, every normal install, upgrade, repair, and documented npm run db:migrate path reaches this call without permission to execute it, because migration 0027's finalizer revoked that temporary grant. Only the new CI-specific wrapper restores the grant, while scripts/install.sh, scripts/setup.sh, and scripts/repair.sh still invoke the raw migrator, so supported deployments fail before 0028 can be applied. Make the supported migration path perform the failure-safe bootstrap and cleanup, including when 0027 and 0028 are both pending.
Useful? React with 👍 / 👎.
| if (action.kind === 'install' || action.kind === 'enable') { | ||
| const nextSelection = [...new Set([...selectedMcpIds, mcpId])] | ||
| setSelectedMcpIds(nextSelection) | ||
| void saveAndInstallSelectedMcps(nextSelection) |
There was a problem hiding this comment.
Make the Enable MCP action actually enable the MCP
When an installed MCP is disabled by config.overrides[mcpId].enabled === false, this branch only adds it to requiredMcps and calls the selection saver, which preserves that same disabled override at lines 568-570. Because the MCP is already installed, the install endpoint is skipped, the refreshed overview remains disabled, and clicking “Enable MCP” repeatedly shows the same button. Update the matching override or installation authority to set enabled: true rather than routing enable through the unchanged selection flow.
Useful? React with 👍 / 👎.
| } | ||
| await loadMcpPresentation() | ||
| await loadTask() | ||
| if (res.status === 202 && typeof payload.error === 'string') setMcpActionError(payload.error) |
There was a problem hiding this comment.
Warn when recovery continuation remains pending
When Redis enqueueing fails after a local or packet recovery mutation commits, the action route returns HTTP 202 with continuationStatus: 'pending' but no error. This condition only surfaces 202 responses that contain payload.error, so the UI reloads, removes the consumed recovery control, and gives the operator no indication that execution still needs a continuation wake-up. Handle the pending continuation status explicitly or include an operator-facing warning in those route responses.
Useful? React with 👍 / 👎.
| const actions = !state.localEvidenceAvailable || terminalized || marker.state !== 'current' | ||
| ? [] | ||
| : marker.allowedActions.map((action) => canonicalRecoveryAction(marker, action)).filter((action): action is CanonicalMcpOperatorAction => action !== null) |
There was a problem hiding this comment.
Hide recovery actions until sibling work is quiescent
When a recovery marker exists while the task remains running because another package is running, awaiting review, or holds a live lease, this predicate still emits the marker's actions. The protected recovery routines require an approved task and quiescent siblings, so every displayed control in that state deterministically returns 409 until reconciliation finishes. Include task status and sibling lease/review barriers in the action predicate and render the waiting state without controls.
Useful? React with 👍 / 👎.
| const valid = assembly && delivery && terminal && audit.terminalAt !== null | ||
| && evidence?.workPackageId === audit.workPackageId | ||
| && evidence.agentRunId === audit.agentRunId | ||
| && evidence.state === 'terminal' | ||
| && packetTerminalTupleIsValid({ assembly, delivery, terminal }) |
There was a problem hiding this comment.
Validate all retained evidence before reporting terminal state
When a terminal runtime audit and local-evidence row are coherent but the run-linked completion artifact, host ledger, or repository reviews are missing or mismatched, this predicate still reports trusted terminal success or failure. The loader never reads those records, so incomplete or conflicting retained evidence is presented as terminal instead of unavailable. Join and validate the complete retained tuple before setting state: 'terminal'.
Useful? React with 👍 / 👎.
Source Issue
Refs #180
Refs #172
Status
Draft implementation PR. This branch now includes the exact current #198/S4 base and the bounded repairs requested by the eight open review findings. Exact-head Contract and Web gates are green; the PR remains draft for review.
What changed
architecture/issue-179-context-packet-evidenceatb1c99d6cd5cc3906dc453fd7f6e1df8d58036534as a semantic union. The S4 role inventory and Web workflow retain every S3/S4 security, recovery, queue, PostgreSQL, and Redis proof.forge_local_evidence_readerprincipal. There is no ordinary-application fallback. Only explicit safe columns are granted or projected.bigintand timestamp values before applying its existing strict decision. Invalid, unsafe, or non-canonical values still fail closed; a valid active row now reaches the recovery routine and its expected stale-state response.{ state, submittedAt }; other delivery arms remain exact one-key objects.Production proof
Web CI now creates a pristine S5 database from the provisioned S4 database and runs one mandatory real PostgreSQL/Redis-backed test:
controller identity, so the production action route crosses the same
fail-closed gate as a deployment;
claimToken,grantNonce,effectiveGrant, and their stored values;1/1, zero skips, and oneS5_POSTGRES_HTTP_AUTHORIZATION_OKmarker.Local PostgreSQL 16.14 and Redis are reachable, but the shared local PostgreSQL instance does not contain the CI-only
forge_e2e,forge_app_test, orforge_local_evidence_readerroles. The mandatory service proof is therefore intentionally left to the hosted role-provisioned job rather than replaced with mocks or local shared-role mutation.Local validation
npm run lint -- --max-warnings=0npx tsc --noEmitnpm run buildgit diff --checkHosted validation
30570186582, job90964670623— passed.30570158070, job90964547212— passed end to end.S5_POSTGRES_HTTP_AUTHORIZATION_OKonce.S4_CROSS_SINK_PRODUCTION_SENTINEL_OKonce.Exact integration
architecture/issue-179-context-packet-evidenceb1c99d6cd5cc3906dc453fd7f6e1df8d580365343a46904b291ddfa823f9793a7ee653832323646c3bf208949f8b0ccf9a351e545ba4307ca3770759c7d2dcdad8bce973e349ba5da7989632697520a8and current S4 base above.Remaining evidence boundary
The existing real-browser D1-to-D2 pointer and stale-reconfirmation cases run in the bridge. The plan-review PostgreSQL concurrency cases remain classified by the inherited bridge as requiring signed activation; this PR does not relabel or bypass that security gate.