[K5] Add bounded Forge port and Forgejo reference adapter #22

Merged
adrian.tkacz merged 11 commits from issue-9-forge-port into develop 2026-09-26 21:32:20 +00:00
Member

Closes #9 after owner integration; keep issue open during review.

Forge port and Forgejo reference adapter for opaque identities, normalized observations/receipts, conditional effects, and shared fake/reference conformance. No merge or deploy capability.

Owner-authorized asymmetric PR authority: exact accepted candidate commit SHA is materialized as a Kernel snapshot ref by one absent-target atomic Git CAS under an externally enforced, bound write-once isolation capability held through POST and verification. No pre-read or adoption of existing same-SHA ref; the mutable working branch is not the PR head. Without isolation, pr is unsupported before writes. Moving target base SHA is receipt provenance, not a changed repository/branch authority identity. No live server-side isolation provider or deployed protection is claimed. Unreadable snapshot verification and malformed producer evidence remain UNKNOWN, not authority conflict or acceptance.

Current HEAD validation: make check (471 passed, formatting/lint/typing/import/secrets/diff), make package-check passed. Earlier make audit reported no known vulnerabilities; no dependencies/tooling changed. No live Forgejo integration was run. Delivery ledger begins #9 (comment); owner decision and remediation 10 are #9 (comment) and #9 (comment).

Closes #9 after owner integration; keep issue open during review. Forge port and Forgejo reference adapter for opaque identities, normalized observations/receipts, conditional effects, and shared fake/reference conformance. No merge or deploy capability. Owner-authorized asymmetric PR authority: exact accepted candidate commit SHA is materialized as a Kernel snapshot ref by one absent-target atomic Git CAS under an externally enforced, bound write-once isolation capability held through POST and verification. No pre-read or adoption of existing same-SHA ref; the mutable working branch is not the PR head. Without isolation, `pr` is unsupported before writes. Moving target base SHA is receipt provenance, not a changed repository/branch authority identity. No live server-side isolation provider or deployed protection is claimed. Unreadable snapshot verification and malformed producer evidence remain UNKNOWN, not authority conflict or acceptance. Current HEAD validation: `make check` (471 passed, formatting/lint/typing/import/secrets/diff), `make package-check` passed. Earlier `make audit` reported no known vulnerabilities; no dependencies/tooling changed. No live Forgejo integration was run. Delivery ledger begins https://forgejo.creatidy.com/Creatidy/creatidy-kernel/issues/9#issuecomment-11798; owner decision and remediation 10 are https://forgejo.creatidy.com/Creatidy/creatidy-kernel/issues/9#issuecomment-11884 and https://forgejo.creatidy.com/Creatidy/creatidy-kernel/issues/9#issuecomment-11922.
feat: add bounded Forge port and Forgejo adapter
All checks were successful
ci / check (pull_request) Successful in 20s
ec8f83c05c
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: ec8f83c05c
Linked issue: #9

Summary

The port direction, inward dependency boundary, fail-closed receipts, conditional single-ref push, and absence of merge capability align with the selected issue and ADRs. The focused suite and repository gates pass, but producer-shape and trusted-transport gaps leave core acceptance criteria unmet.

Intent / Acceptance Evidence

Issue #9 criterion Evidence at frozen head Result
Fake and Forgejo share conformance Parametrized suite covers both adapters, but synthetic status payloads and provider validation differ from production Partial
Opaque qualified identities Core references are qualified and Forgejo paths remain adapter-local Met, subject to fake gap below
Distinct safe pagination/absence/stale/unknown semantics Pagination and fail-closed reads are covered; branch reconciliation can erase source uncertainty Partial
Exact authorized effects and validated domain receipts Authorization predicate and receipt checks exist; Git transport does not control inherited hooks/config Not met
PR creation without automatic merge PR artifact/replay covered and no merge capability is exposed Met

Architecture / Information Flow

Dependency direction remains adapters -> ports -> core, verified by import-linter. Forgejo payloads do not enter core. The main boundary defects are at the real producer schema and credential-bearing Git process.

Blocking Issues

  1. Blocking defect: real Forgejo check observations always become incomplete. src/creatidy_kernel/adapters/forgejo.py:187-200 requires a sha field on each item returned by GET /repos/{owner}/{repo}/commits/{ref}/statuses. The exact Forgejo instance Swagger for CommitStatusList defines context, timestamps, creator, description, id, status, target_url, and url, but no sha; the requested {ref} already binds the subject. The synthetic fixture adds sha at tests/test_forge.py:58-63, masking the producer mismatch. A real successful response therefore raises inside _field(data, "sha") and returns Page((), None, False), so exact-head CI evidence cannot be observed. Build the observation revision from the requested subject and test a producer-shaped Forgejo payload.

  2. Security/privacy note and blocking defect: the conditional push executes inherited Git hooks/config in the credential-bearing controller. src/creatidy_kernel/adapters/forgejo_transport.py:117-152 runs both Git commands in the supplied worktree, inherits the full environment, and does not disable hooks or isolate system/global/local configuration. A configured pre-push hook can execute arbitrary code with controller authority, and url.*.insteadOf can redirect the lexically validated URL after ConditionalGitTransport.__init__ checks it at lines 91-108. This conflicts with docs/architecture/threat-model.md:23 and means exact-destination authorization is not enforced by the transport. Use a controlled Git configuration/hooks boundary or a trusted dedicated Git repository and add malicious-hook and URL-rewrite fixtures.

  3. Blocking defect: branch reconciliation can turn a deliberately uncertain source race into acceptance. The initial branch path returns UNKNOWN when the exact source moves after the push (src/creatidy_kernel/adapters/forgejo.py:238-258), but reconcile() checks only the target revision at lines 303-309. The fake has the same gap at src/creatidy_kernel/adapters/fake_forge.py:272-278. After a lost reply and source movement, reconciliation therefore upgrades the same effect to ACCEPTED despite no proof that the source precondition held at mutation time. Preserve that uncertainty by rechecking the branch source during reconciliation and add the race to the shared suite.

  4. Blocking conformance gap: the fake accepts revision providers rejected by the Forgejo adapter. src/creatidy_kernel/adapters/fake_forge.py:204-230 strips an optional forgejo: prefix without validating the provider, whereas ForgejoForge._authorized() rejects foreign providers at src/creatidy_kernel/adapters/forgejo.py:212-222. A github: revision can therefore pass fake tests while being impossible through the reference adapter. Enforce equivalent configured-provider constraints and cover foreign-provider effects in the shared suite.

Non-blocking Suggestions

None beyond the blocking remediation.

Questions for the Author

None.

Security / Privacy Notes

The HTTP transport correctly requires HTTPS, rejects embedded credentials, disables redirects, bounds response bytes, and obtains its token lazily. The Git hook/config inheritance above remains the material security boundary issue.

Test / Documentation Gaps

Add producer-shaped CommitStatus fixtures, malicious Git hook/config fixtures, a lost-reply plus moved-source reconciliation case, and cross-provider conformance cases. No documentation expansion is required.

Follow-Up Candidates

None. These defects are required by issue #9 and belong in this PR.

Overall Recommendation

REQUEST_CHANGES

The recommendation applies only to frozen HEAD ec8f83c05ceb38162845b854ef7b0f4b85be62d2. Blocking findings: four. Validation independently rerun: uv run --locked pytest tests/test_forge.py tests/test_forge_transports.py (22 passed), make check (201 passed), make package-check (passed), and frozen diff whitespace checks (passed). The shared kilo identity authored the PR, so this is intentionally persisted as COMMENT; it is not a formal Forgejo REQUEST_CHANGES state.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** issue-9-forge-port -> develop **Frozen head:** ec8f83c05ceb38162845b854ef7b0f4b85be62d2 **Linked issue:** #9 ### Summary The port direction, inward dependency boundary, fail-closed receipts, conditional single-ref push, and absence of merge capability align with the selected issue and ADRs. The focused suite and repository gates pass, but producer-shape and trusted-transport gaps leave core acceptance criteria unmet. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence at frozen head | Result | | --- | --- | --- | | Fake and Forgejo share conformance | Parametrized suite covers both adapters, but synthetic status payloads and provider validation differ from production | Partial | | Opaque qualified identities | Core references are qualified and Forgejo paths remain adapter-local | Met, subject to fake gap below | | Distinct safe pagination/absence/stale/unknown semantics | Pagination and fail-closed reads are covered; branch reconciliation can erase source uncertainty | Partial | | Exact authorized effects and validated domain receipts | Authorization predicate and receipt checks exist; Git transport does not control inherited hooks/config | Not met | | PR creation without automatic merge | PR artifact/replay covered and no merge capability is exposed | Met | ### Architecture / Information Flow Dependency direction remains `adapters -> ports -> core`, verified by import-linter. Forgejo payloads do not enter core. The main boundary defects are at the real producer schema and credential-bearing Git process. ### Blocking Issues 1. **Blocking defect: real Forgejo check observations always become incomplete.** `src/creatidy_kernel/adapters/forgejo.py:187-200` requires a `sha` field on each item returned by `GET /repos/{owner}/{repo}/commits/{ref}/statuses`. The exact Forgejo instance Swagger for `CommitStatusList` defines `context`, timestamps, creator, description, `id`, `status`, `target_url`, and `url`, but no `sha`; the requested `{ref}` already binds the subject. The synthetic fixture adds `sha` at `tests/test_forge.py:58-63`, masking the producer mismatch. A real successful response therefore raises inside `_field(data, "sha")` and returns `Page((), None, False)`, so exact-head CI evidence cannot be observed. Build the observation revision from the requested subject and test a producer-shaped Forgejo payload. 2. **Security/privacy note and blocking defect: the conditional push executes inherited Git hooks/config in the credential-bearing controller.** `src/creatidy_kernel/adapters/forgejo_transport.py:117-152` runs both Git commands in the supplied worktree, inherits the full environment, and does not disable hooks or isolate system/global/local configuration. A configured `pre-push` hook can execute arbitrary code with controller authority, and `url.*.insteadOf` can redirect the lexically validated URL after `ConditionalGitTransport.__init__` checks it at lines 91-108. This conflicts with `docs/architecture/threat-model.md:23` and means exact-destination authorization is not enforced by the transport. Use a controlled Git configuration/hooks boundary or a trusted dedicated Git repository and add malicious-hook and URL-rewrite fixtures. 3. **Blocking defect: branch reconciliation can turn a deliberately uncertain source race into acceptance.** The initial branch path returns `UNKNOWN` when the exact source moves after the push (`src/creatidy_kernel/adapters/forgejo.py:238-258`), but `reconcile()` checks only the target revision at lines 303-309. The fake has the same gap at `src/creatidy_kernel/adapters/fake_forge.py:272-278`. After a lost reply and source movement, reconciliation therefore upgrades the same effect to `ACCEPTED` despite no proof that the source precondition held at mutation time. Preserve that uncertainty by rechecking the branch source during reconciliation and add the race to the shared suite. 4. **Blocking conformance gap: the fake accepts revision providers rejected by the Forgejo adapter.** `src/creatidy_kernel/adapters/fake_forge.py:204-230` strips an optional `forgejo:` prefix without validating the provider, whereas `ForgejoForge._authorized()` rejects foreign providers at `src/creatidy_kernel/adapters/forgejo.py:212-222`. A `github:` revision can therefore pass fake tests while being impossible through the reference adapter. Enforce equivalent configured-provider constraints and cover foreign-provider effects in the shared suite. ### Non-blocking Suggestions None beyond the blocking remediation. ### Questions for the Author None. ### Security / Privacy Notes The HTTP transport correctly requires HTTPS, rejects embedded credentials, disables redirects, bounds response bytes, and obtains its token lazily. The Git hook/config inheritance above remains the material security boundary issue. ### Test / Documentation Gaps Add producer-shaped `CommitStatus` fixtures, malicious Git hook/config fixtures, a lost-reply plus moved-source reconciliation case, and cross-provider conformance cases. No documentation expansion is required. ### Follow-Up Candidates None. These defects are required by issue #9 and belong in this PR. ### Overall Recommendation REQUEST_CHANGES The recommendation applies only to frozen HEAD `ec8f83c05ceb38162845b854ef7b0f4b85be62d2`. Blocking findings: four. Validation independently rerun: `uv run --locked pytest tests/test_forge.py tests/test_forge_transports.py` (22 passed), `make check` (201 passed), `make package-check` (passed), and frozen diff whitespace checks (passed). The shared `kilo` identity authored the PR, so this is intentionally persisted as `COMMENT`; it is not a formal Forgejo `REQUEST_CHANGES` state.
fix: harden Forgejo observations and conditional push
All checks were successful
ci / check (pull_request) Successful in 19s
36351d0081
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: 36351d0081
Linked issue: #9

Summary

The cycle-1 remediation addresses the prior status-payload, inherited Git config/hooks, moved-source, and provider-conformance findings. Dependency direction remains adapters -> ports -> core, exact-head CI run #1960 is green, and no merge capability is exposed. The replacement head still has blocking ambiguity in operation-key reconciliation, PR repository validation, absence/pagination semantics, and stale-result classification.

Intent / Acceptance Evidence

Issue #9 criterion Evidence at frozen head Result
Fake and Forgejo share required-operation/receipt conformance Shared suite covers happy-path operations and the prior provider gap; malformed change-handle/schema behavior still differs Partial
Identities remain opaque and qualified Core values are provider-qualified and adapter payload parsing stays at the edge Met
Pagination, permission-limited absence, stale, unsupported, and unknown remain distinct and safe Direct 404 absence is never represented; short pages can truncate; Git stderr can be mislabeled stale Not met
Only authorized exact-scope effects occur and receipts are domain-validated Authorization and CAS are present, but marker collisions and missing PR repository validation can acknowledge the wrong artifact Not met
PR opening is tested and automatic merge is absent PR creation/replay is covered and merge is not exposed Met, subject to receipt/reconciliation defects below

Architecture / Information Flow

The code preserves the accepted inward dependency boundary and keeps Forgejo payloads out of core. The isolated bare Git push materially improves the credential boundary. Remaining defects are at the untrusted producer/reconciliation boundary required by ADR 0003, ADR 0005, and docs/architecture/contracts.md.

Blocking Issues

  1. Blocking defect: different operation keys can generate the same durable PR marker. src/creatidy_kernel/adapters/forgejo.py:266-267,313-314 and src/creatidy_kernel/adapters/fake_forge.py:262,299 concatenate effect_key and request_digest with :. OperationKey permits arbitrary nonempty strings, so ("a:b", "c") and ("a", "b:c") collide. With otherwise identical PR metadata, reconciliation can acknowledge one logical effect using another effect's artifact. Use an unambiguous canonical encoding or constrain and test the key grammar.

  2. Blocking defect: accepted PR receipts do not validate the repository identities in the Forgejo response. src/creatidy_kernel/adapters/forgejo.py:147-158,280-297,327-352 validates number, refs, and SHAs but ignores head.repo and base.repo, then manufactures a change handle under the requested repository. A malformed, stale, or cross-repository response with matching names/SHAs can therefore be accepted for the wrong artifact. Validate producer-shaped repository identity on creation and reconciliation; if fork PRs are intended, represent their head repository explicitly.

  3. Blocking contract gap: authoritative absence is never represented. src/creatidy_kernel/adapters/forgejo.py:97-102,112-117,130-139 maps every direct 404 to UNKNOWN; src/creatidy_kernel/adapters/fake_forge.py:117-133 also maps known-missing in-memory branches and changes to UNKNOWN. Presence.ABSENT is consequently unused, and tests/test_forge.py:52-57 codifies the omission. Define which observations establish authoritative absence and separately cover ABSENT, permission-limited INACCESSIBLE, and transport/eventual-consistency UNKNOWN.

  4. Blocking defect: hostile or incidental stderr can turn an unknown push into STALE. src/creatidy_kernel/adapters/forgejo_transport.py:190-195 concatenates stdout and stderr and classifies any output containing stale info as a CAS rejection. Remote sideband/error text is not a validated porcelain receipt; a failed or uncertain push whose stderr contains that phrase is incorrectly made retryable as stale. Parse an exact structured porcelain rejection for the authorized target ref and test misleading stderr as UNKNOWN.

  5. Blocking pagination defect: a short page is treated as authoritative completion without producer pagination metadata. src/creatidy_kernel/adapters/forgejo.py:160-200 sets complete=True whenever the payload is shorter than the requested size, while the HTTP transport discards response headers. If Forgejo applies a lower server-side cap, page one is falsely terminal and later PRs/checks cannot be observed. Preserve authoritative pagination metadata or continue safely to a terminal page; add a server-cap fixture.

  6. Blocking boundedness defect: PR reconciliation can issue unbounded authenticated requests. src/creatidy_kernel/adapters/forgejo.py:313-356 scans every PR page and fetches each PR individually with no total page/request/time budget. A large or adversarial repository can consume unbounded controller time and API quota despite per-request timeouts. Add a configurable reconciliation budget and return UNKNOWN without treating budget exhaustion as absence.

Non-blocking Suggestions

None beyond the required remediation.

Questions for the Author

None.

Security / Privacy Notes

The remediation now isolates Git hooks/configuration, constrains the remote, disables redirects, and keeps credentials outside the workload-controlled worktree. Findings 4 and 6 remain availability/authority-boundary issues because forge output is untrusted.

Test / Documentation Gaps

Add shared collision, repository-mismatch, authoritative-absence, server-cap, scan-budget, and misleading-stderr fixtures. Also align malformed/zero/negative change-handle behavior between the fake and Forgejo adapter; the current fake can accept synthetic identities the reference adapter rejects. No documentation expansion is required.

Follow-Up Candidates

None. These gaps are part of issue #9's exact-effect, pagination, conformance, and reconciliation acceptance criteria.

Overall Recommendation

REQUEST_CHANGES

This recommendation applies only to frozen HEAD 36351d00811113f4d22fa8aebb6f5ff55e2a2056. Blocking findings: six. Exact-head Forgejo Actions run #1960 reports success; PR evidence reports make check with 211 passing tests and make package-check passing. I did not execute untrusted PR code in the reviewer context. The server's workflow-job endpoint returned 404, so job logs were not independently inspected. Live Forgejo integration remains unrun. The shared kilo identity authored the PR, so this review is intentionally persisted as COMMENT; it is not a formal Forgejo REQUEST_CHANGES state.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** issue-9-forge-port -> develop **Frozen head:** 36351d00811113f4d22fa8aebb6f5ff55e2a2056 **Linked issue:** #9 ### Summary The cycle-1 remediation addresses the prior status-payload, inherited Git config/hooks, moved-source, and provider-conformance findings. Dependency direction remains `adapters -> ports -> core`, exact-head CI run #1960 is green, and no merge capability is exposed. The replacement head still has blocking ambiguity in operation-key reconciliation, PR repository validation, absence/pagination semantics, and stale-result classification. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence at frozen head | Result | | --- | --- | --- | | Fake and Forgejo share required-operation/receipt conformance | Shared suite covers happy-path operations and the prior provider gap; malformed change-handle/schema behavior still differs | Partial | | Identities remain opaque and qualified | Core values are provider-qualified and adapter payload parsing stays at the edge | Met | | Pagination, permission-limited absence, stale, unsupported, and unknown remain distinct and safe | Direct 404 absence is never represented; short pages can truncate; Git stderr can be mislabeled stale | Not met | | Only authorized exact-scope effects occur and receipts are domain-validated | Authorization and CAS are present, but marker collisions and missing PR repository validation can acknowledge the wrong artifact | Not met | | PR opening is tested and automatic merge is absent | PR creation/replay is covered and merge is not exposed | Met, subject to receipt/reconciliation defects below | ### Architecture / Information Flow The code preserves the accepted inward dependency boundary and keeps Forgejo payloads out of core. The isolated bare Git push materially improves the credential boundary. Remaining defects are at the untrusted producer/reconciliation boundary required by ADR 0003, ADR 0005, and `docs/architecture/contracts.md`. ### Blocking Issues 1. **Blocking defect: different operation keys can generate the same durable PR marker.** `src/creatidy_kernel/adapters/forgejo.py:266-267,313-314` and `src/creatidy_kernel/adapters/fake_forge.py:262,299` concatenate `effect_key` and `request_digest` with `:`. `OperationKey` permits arbitrary nonempty strings, so `("a:b", "c")` and `("a", "b:c")` collide. With otherwise identical PR metadata, reconciliation can acknowledge one logical effect using another effect's artifact. Use an unambiguous canonical encoding or constrain and test the key grammar. 2. **Blocking defect: accepted PR receipts do not validate the repository identities in the Forgejo response.** `src/creatidy_kernel/adapters/forgejo.py:147-158,280-297,327-352` validates number, refs, and SHAs but ignores `head.repo` and `base.repo`, then manufactures a change handle under the requested repository. A malformed, stale, or cross-repository response with matching names/SHAs can therefore be accepted for the wrong artifact. Validate producer-shaped repository identity on creation and reconciliation; if fork PRs are intended, represent their head repository explicitly. 3. **Blocking contract gap: authoritative absence is never represented.** `src/creatidy_kernel/adapters/forgejo.py:97-102,112-117,130-139` maps every direct 404 to `UNKNOWN`; `src/creatidy_kernel/adapters/fake_forge.py:117-133` also maps known-missing in-memory branches and changes to `UNKNOWN`. `Presence.ABSENT` is consequently unused, and `tests/test_forge.py:52-57` codifies the omission. Define which observations establish authoritative absence and separately cover `ABSENT`, permission-limited `INACCESSIBLE`, and transport/eventual-consistency `UNKNOWN`. 4. **Blocking defect: hostile or incidental stderr can turn an unknown push into `STALE`.** `src/creatidy_kernel/adapters/forgejo_transport.py:190-195` concatenates stdout and stderr and classifies any output containing `stale info` as a CAS rejection. Remote sideband/error text is not a validated porcelain receipt; a failed or uncertain push whose stderr contains that phrase is incorrectly made retryable as stale. Parse an exact structured porcelain rejection for the authorized target ref and test misleading stderr as `UNKNOWN`. 5. **Blocking pagination defect: a short page is treated as authoritative completion without producer pagination metadata.** `src/creatidy_kernel/adapters/forgejo.py:160-200` sets `complete=True` whenever the payload is shorter than the requested size, while the HTTP transport discards response headers. If Forgejo applies a lower server-side cap, page one is falsely terminal and later PRs/checks cannot be observed. Preserve authoritative pagination metadata or continue safely to a terminal page; add a server-cap fixture. 6. **Blocking boundedness defect: PR reconciliation can issue unbounded authenticated requests.** `src/creatidy_kernel/adapters/forgejo.py:313-356` scans every PR page and fetches each PR individually with no total page/request/time budget. A large or adversarial repository can consume unbounded controller time and API quota despite per-request timeouts. Add a configurable reconciliation budget and return `UNKNOWN` without treating budget exhaustion as absence. ### Non-blocking Suggestions None beyond the required remediation. ### Questions for the Author None. ### Security / Privacy Notes The remediation now isolates Git hooks/configuration, constrains the remote, disables redirects, and keeps credentials outside the workload-controlled worktree. Findings 4 and 6 remain availability/authority-boundary issues because forge output is untrusted. ### Test / Documentation Gaps Add shared collision, repository-mismatch, authoritative-absence, server-cap, scan-budget, and misleading-stderr fixtures. Also align malformed/zero/negative change-handle behavior between the fake and Forgejo adapter; the current fake can accept synthetic identities the reference adapter rejects. No documentation expansion is required. ### Follow-Up Candidates None. These gaps are part of issue #9's exact-effect, pagination, conformance, and reconciliation acceptance criteria. ### Overall Recommendation REQUEST_CHANGES This recommendation applies only to frozen HEAD `36351d00811113f4d22fa8aebb6f5ff55e2a2056`. Blocking findings: six. Exact-head Forgejo Actions run #1960 reports success; PR evidence reports `make check` with 211 passing tests and `make package-check` passing. I did not execute untrusted PR code in the reviewer context. The server's workflow-job endpoint returned 404, so job logs were not independently inspected. Live Forgejo integration remains unrun. The shared `kilo` identity authored the PR, so this review is intentionally persisted as `COMMENT`; it is not a formal Forgejo `REQUEST_CHANGES` state.
kilo left a comment

Architecture / Model-Correction Review

Frozen HEAD: 36351d00811113f4d22fa8aebb6f5ff55e2a2056
Base: develop@8877b97b83629e942e1b8725dfd02bb70c658b27
Scope: issue #9, ADR 0004/0005, docs/architecture/contracts.md, whole PR, and cycle-2 F5-F10. This is not a replacement full-review verdict and is not a formal APPROVE.

Verdict

BOUNDED_CORRECTION

All six blockers are remediable inside issue #9's accepted Forge boundary. They require no merge capability, fork-universal model, new Program semantics, owner authority, or architecture/policy decision.

Finding Disposition

  • F5 marker collision: valid. Use a canonical unambiguous encoding of the operation identity, shared by creation/reconciliation and fake/reference adapters, with collision tests.
  • F6 PR repository identity: valid, with bounded scope. Validate producer-shaped base.repo.full_name and, for this same-repository PR operation, head.repo.full_name against the authorized repository before acknowledging creation or reconciliation. Treat missing/mismatched/fork-shaped data as unsupported/unknown; do not add universal fork support or a new core field.
  • F7 absence: valid contract gap, but mapping every 404 to ABSENT would be invalid. Preserve UNKNOWN when 404 may hide permission or consistency state. Add an explicit adapter/transport evidence path for authoritative direct absence and test it separately from INACCESSIBLE and UNKNOWN; make the fake model the same distinctions.
  • F8 stale classification: valid. Recognize only the exact structured porcelain rejection for the authorized ref; arbitrary stderr/sideband text remains UNKNOWN.
  • F9 pagination: valid, but page exhaustion is not domain-absence proof. Preserve Forgejo pagination metadata or continue under a bounded cursor strategy; only mark enumeration complete from authoritative pagination evidence. Never convert a completed bounded search into authoritative effect absence.
  • F10 unbounded reconciliation: valid. Add a finite configurable page/request budget. Budget exhaustion, malformed cursors, repeated cursors, transport ambiguity, and permission limits return UNKNOWN.

Bounded Instructions

  1. Keep core provider-neutral and retain FOUND/ABSENT/INACCESSIBLE/UNKNOWN; refine transport/adapter evidence rather than inventing a universal forge model.
  2. Keep PR effects same-repository only for issue #9. Fail closed on fork-shaped or repository-mismatched payloads.
  3. Make pagination provenance and reconciliation budgets explicit at the adapter boundary; do not infer absence from short pages or budget exhaustion.
  4. Add shared fake/Forgejo conformance cases for marker collisions, repository mismatch/fork-shaped payloads, authoritative absence versus concealed/unknown 404, server caps/cursors, scan exhaustion, and misleading stderr.
  5. Preserve no-merge capability and existing adapters -> ports -> core direction.

No genuine owner authority, security/privacy choice, scope expansion, or policy decision is materially required for these corrections.

## Architecture / Model-Correction Review **Frozen HEAD:** `36351d00811113f4d22fa8aebb6f5ff55e2a2056` **Base:** `develop@8877b97b83629e942e1b8725dfd02bb70c658b27` **Scope:** issue #9, ADR 0004/0005, `docs/architecture/contracts.md`, whole PR, and cycle-2 F5-F10. This is not a replacement full-review verdict and is not a formal APPROVE. ### Verdict **BOUNDED_CORRECTION** All six blockers are remediable inside issue #9's accepted Forge boundary. They require no merge capability, fork-universal model, new Program semantics, owner authority, or architecture/policy decision. ### Finding Disposition - **F5 marker collision: valid.** Use a canonical unambiguous encoding of the operation identity, shared by creation/reconciliation and fake/reference adapters, with collision tests. - **F6 PR repository identity: valid, with bounded scope.** Validate producer-shaped `base.repo.full_name` and, for this same-repository PR operation, `head.repo.full_name` against the authorized repository before acknowledging creation or reconciliation. Treat missing/mismatched/fork-shaped data as unsupported/unknown; do not add universal fork support or a new core field. - **F7 absence: valid contract gap, but mapping every 404 to `ABSENT` would be invalid.** Preserve `UNKNOWN` when 404 may hide permission or consistency state. Add an explicit adapter/transport evidence path for authoritative direct absence and test it separately from `INACCESSIBLE` and `UNKNOWN`; make the fake model the same distinctions. - **F8 stale classification: valid.** Recognize only the exact structured porcelain rejection for the authorized ref; arbitrary stderr/sideband text remains `UNKNOWN`. - **F9 pagination: valid, but page exhaustion is not domain-absence proof.** Preserve Forgejo pagination metadata or continue under a bounded cursor strategy; only mark enumeration complete from authoritative pagination evidence. Never convert a completed bounded search into authoritative effect absence. - **F10 unbounded reconciliation: valid.** Add a finite configurable page/request budget. Budget exhaustion, malformed cursors, repeated cursors, transport ambiguity, and permission limits return `UNKNOWN`. ### Bounded Instructions 1. Keep core provider-neutral and retain `FOUND/ABSENT/INACCESSIBLE/UNKNOWN`; refine transport/adapter evidence rather than inventing a universal forge model. 2. Keep PR effects same-repository only for issue #9. Fail closed on fork-shaped or repository-mismatched payloads. 3. Make pagination provenance and reconciliation budgets explicit at the adapter boundary; do not infer absence from short pages or budget exhaustion. 4. Add shared fake/Forgejo conformance cases for marker collisions, repository mismatch/fork-shaped payloads, authoritative absence versus concealed/unknown 404, server caps/cursors, scan exhaustion, and misleading stderr. 5. Preserve no-merge capability and existing `adapters -> ports -> core` direction. No genuine owner authority, security/privacy choice, scope expansion, or policy decision is materially required for these corrections.
fix: bound Forge reconciliation and validate PR identity
All checks were successful
ci / check (pull_request) Successful in 19s
416e817499
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: 416e817499
Linked issue: #9

Summary

The cycle-2 remediation fixes the prior marker, same-repository PR, server-cap pagination, stale-output, and basic reconciliation findings. Dependency direction remains adapters -> ports -> core, exact-head CI is green, and no merge capability is exposed. The replacement head still has blocking transport-boundary, finite-budget, and absence/conformance gaps.

Intent / Acceptance Evidence

Issue #9 criterion Evidence at frozen head Result
Fake and Forgejo share required-operation/receipt conformance Main effect suite is parametrized, but malformed cursor and repository-absence behavior still diverge or remain uncovered Partial
Identities remain opaque and qualified Core references remain provider-qualified; Forgejo payloads stay adapter-local Met
Pagination, permission-limited absence, stale, unsupported and unknown remain distinct and safe Server-cap pagination and stale parsing improved, but repository absence cannot consume authoritative evidence and the request budget is exceeded on acceptance Not met
Only authorized exact-scope effects occur; transport success is not acknowledgement Authorization and CAS inputs are present, but Git exit zero is accepted without validating the porcelain domain receipt; object discovery trusts workload-controlled Git indirection Not met
PR opening tested; automatic merge absent PR creation/replay is covered and merge is not exposed Met

Architecture / Information Flow

The code preserves the accepted inward dependency boundary. The remaining defects sit at the trusted-controller/untrusted-worktree and external-forge boundaries governed by ADR 0003, ADR 0005, docs/architecture/contracts.md, and the threat model.

Blocking Issues

  1. Security/privacy note and blocking defect: workload Git metadata can redirect the credential-bearing controller to an arbitrary object database. src/creatidy_kernel/adapters/forgejo_transport.py:106-107,163-175 only verifies that worktree is a directory, then trusts git rev-parse --git-path objects and exports the result as GIT_ALTERNATE_OBJECT_DIRECTORIES. A worker-controlled .git file, linked-worktree/common-dir metadata, symlink, or alternates file can escape to another controller-readable repository; the controller then reads and can push reachable objects with forge credentials. This also has a validation/use race. tests/test_forge_transports.py:102-165 covers hooks and config rewrites but not Git-dir/object-dir indirection. Canonically validate and pin the repository/object source, or require a separately trusted object store, with malicious indirection/symlink fixtures.

  2. Blocking defect: successful pushes are acknowledged from a tool exit code, not a validated domain receipt. src/creatidy_kernel/adapters/forgejo_transport.py:196-198 maps any zero git push return code to ACCEPTED without parsing the successful porcelain record for the exact source object, destination ref, and single requested update. tests/test_forge_transports.py:50-54 codifies the gap by accepting arbitrary stdout ok. ADR 0003 explicitly says acknowledgement requires a validated domain receipt, not a tool exit code. Validate the exact success/up-to-date porcelain record and treat malformed, missing, extra-ref, or contradictory output as UNKNOWN.

  3. Blocking boundedness defect: max_reconcile_requests is exceeded on the successful-match path. src/creatidy_kernel/adapters/forgejo.py:339-355 counts the list and detail requests, but lines 381-384 perform two additional branch() GETs without checking or incrementing the budget. With one matching PR and max_reconcile_requests=2, reconciliation performs four authenticated requests and can return ACCEPTED. The current test only exercises a limit of one at tests/test_forge.py:358-359. Make every network observation consume the same finite budget and test the exact-boundary acceptance path.

  4. Blocking acceptance gap: authoritative repository absence is discarded. HTTPTransport.authoritative_absence() is part of the boundary and is used for branch/change 404s, but ForgejoForge.identity() at src/creatidy_kernel/adapters/forgejo.py:104-117 maps every non-200/non-403 response to UNKNOWN, even when the injected transport explicitly attests authoritative absence. FakeForge.identity() likewise cannot represent known repository absence. This leaves identity absence outside the issue's observation/absence model and shared conformance. Apply the same explicit-evidence rule to repository identity and add shared ABSENT versus INACCESSIBLE versus UNKNOWN cases.

  5. Security/privacy note and blocking boundedness defect: hostile Git output is captured without a byte cap. Every command at src/creatidy_kernel/adapters/forgejo_transport.py:155-158, including network-facing git push, uses capture_output=True. A hostile forge can emit unbounded sideband/stdout/stderr during the timeout and exhaust controller memory. Bound captured output while retaining enough structured porcelain for validation, and add an output-flood fixture.

  6. Blocking boundedness defect: the HTTPS limits are not end-to-end. At src/creatidy_kernel/adapters/forgejo_transport.py:42-50,63,75-85, urllib's timeout is a per-socket-operation timeout rather than a total deadline, so a trickling peer can keep response.read(max_bytes + 1) alive indefinitely; request JSON is also serialized and sent without a request-size limit. Add a total operation deadline and outbound byte bound, with slow-response and oversized-request fixtures.

Non-blocking Suggestions

Align malformed pagination cursors in the shared fake/reference suite. ForgejoForge._page() returns an incomplete page for invalid/zero cursors, while FakeForge._page() raises from int(cursor) or its page check (src/creatidy_kernel/adapters/fake_forge.py:177-187).

Questions for the Author

None.

Security / Privacy Notes

The isolated bare push repository, disabled hooks/config/helpers, redirect refusal, exact stale-output parsing, and same-repository PR validation are material improvements. Findings 1, 5, and 6 remain controller trust/resource-boundary defects.

Test / Documentation Gaps

Add producer-shaped success-porcelain, Git-dir/object-dir escape, exact request-budget, repository-absence, output-flood, slow-response, outbound-size, and shared malformed-cursor cases. No documentation expansion is required.

Follow-Up Candidates

None. These defects belong to issue #9's exact receipt, bounded transport/reconciliation, absence, and shared-conformance criteria.

Overall Recommendation

REQUEST_CHANGES

This recommendation applies only to frozen HEAD 416e81749916746cf41218538487ae28d19fc102. Blocking findings: six. Forgejo Actions run #1963 reports success for the exact head, and git diff --check origin/develop...HEAD passed. I did not execute untrusted PR code in the reviewer context. Forgejo's workflow-job endpoint returned 404, so job logs were not independently inspected; live Forgejo integration remains unrun. The shared kilo identity authored the PR, so this review is intentionally persisted as COMMENT; it is not a formal Forgejo REQUEST_CHANGES state.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** issue-9-forge-port -> develop **Frozen head:** 416e81749916746cf41218538487ae28d19fc102 **Linked issue:** #9 ### Summary The cycle-2 remediation fixes the prior marker, same-repository PR, server-cap pagination, stale-output, and basic reconciliation findings. Dependency direction remains `adapters -> ports -> core`, exact-head CI is green, and no merge capability is exposed. The replacement head still has blocking transport-boundary, finite-budget, and absence/conformance gaps. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence at frozen head | Result | | --- | --- | --- | | Fake and Forgejo share required-operation/receipt conformance | Main effect suite is parametrized, but malformed cursor and repository-absence behavior still diverge or remain uncovered | Partial | | Identities remain opaque and qualified | Core references remain provider-qualified; Forgejo payloads stay adapter-local | Met | | Pagination, permission-limited absence, stale, unsupported and unknown remain distinct and safe | Server-cap pagination and stale parsing improved, but repository absence cannot consume authoritative evidence and the request budget is exceeded on acceptance | Not met | | Only authorized exact-scope effects occur; transport success is not acknowledgement | Authorization and CAS inputs are present, but Git exit zero is accepted without validating the porcelain domain receipt; object discovery trusts workload-controlled Git indirection | Not met | | PR opening tested; automatic merge absent | PR creation/replay is covered and merge is not exposed | Met | ### Architecture / Information Flow The code preserves the accepted inward dependency boundary. The remaining defects sit at the trusted-controller/untrusted-worktree and external-forge boundaries governed by ADR 0003, ADR 0005, `docs/architecture/contracts.md`, and the threat model. ### Blocking Issues 1. **Security/privacy note and blocking defect: workload Git metadata can redirect the credential-bearing controller to an arbitrary object database.** `src/creatidy_kernel/adapters/forgejo_transport.py:106-107,163-175` only verifies that `worktree` is a directory, then trusts `git rev-parse --git-path objects` and exports the result as `GIT_ALTERNATE_OBJECT_DIRECTORIES`. A worker-controlled `.git` file, linked-worktree/common-dir metadata, symlink, or alternates file can escape to another controller-readable repository; the controller then reads and can push reachable objects with forge credentials. This also has a validation/use race. `tests/test_forge_transports.py:102-165` covers hooks and config rewrites but not Git-dir/object-dir indirection. Canonically validate and pin the repository/object source, or require a separately trusted object store, with malicious indirection/symlink fixtures. 2. **Blocking defect: successful pushes are acknowledged from a tool exit code, not a validated domain receipt.** `src/creatidy_kernel/adapters/forgejo_transport.py:196-198` maps any zero `git push` return code to `ACCEPTED` without parsing the successful porcelain record for the exact source object, destination ref, and single requested update. `tests/test_forge_transports.py:50-54` codifies the gap by accepting arbitrary stdout `ok`. ADR 0003 explicitly says acknowledgement requires a validated domain receipt, not a tool exit code. Validate the exact success/up-to-date porcelain record and treat malformed, missing, extra-ref, or contradictory output as `UNKNOWN`. 3. **Blocking boundedness defect: `max_reconcile_requests` is exceeded on the successful-match path.** `src/creatidy_kernel/adapters/forgejo.py:339-355` counts the list and detail requests, but lines 381-384 perform two additional `branch()` GETs without checking or incrementing the budget. With one matching PR and `max_reconcile_requests=2`, reconciliation performs four authenticated requests and can return `ACCEPTED`. The current test only exercises a limit of one at `tests/test_forge.py:358-359`. Make every network observation consume the same finite budget and test the exact-boundary acceptance path. 4. **Blocking acceptance gap: authoritative repository absence is discarded.** `HTTPTransport.authoritative_absence()` is part of the boundary and is used for branch/change 404s, but `ForgejoForge.identity()` at `src/creatidy_kernel/adapters/forgejo.py:104-117` maps every non-200/non-403 response to `UNKNOWN`, even when the injected transport explicitly attests authoritative absence. `FakeForge.identity()` likewise cannot represent known repository absence. This leaves identity absence outside the issue's observation/absence model and shared conformance. Apply the same explicit-evidence rule to repository identity and add shared `ABSENT` versus `INACCESSIBLE` versus `UNKNOWN` cases. 5. **Security/privacy note and blocking boundedness defect: hostile Git output is captured without a byte cap.** Every command at `src/creatidy_kernel/adapters/forgejo_transport.py:155-158`, including network-facing `git push`, uses `capture_output=True`. A hostile forge can emit unbounded sideband/stdout/stderr during the timeout and exhaust controller memory. Bound captured output while retaining enough structured porcelain for validation, and add an output-flood fixture. 6. **Blocking boundedness defect: the HTTPS limits are not end-to-end.** At `src/creatidy_kernel/adapters/forgejo_transport.py:42-50,63,75-85`, urllib's timeout is a per-socket-operation timeout rather than a total deadline, so a trickling peer can keep `response.read(max_bytes + 1)` alive indefinitely; request JSON is also serialized and sent without a request-size limit. Add a total operation deadline and outbound byte bound, with slow-response and oversized-request fixtures. ### Non-blocking Suggestions Align malformed pagination cursors in the shared fake/reference suite. `ForgejoForge._page()` returns an incomplete page for invalid/zero cursors, while `FakeForge._page()` raises from `int(cursor)` or its page check (`src/creatidy_kernel/adapters/fake_forge.py:177-187`). ### Questions for the Author None. ### Security / Privacy Notes The isolated bare push repository, disabled hooks/config/helpers, redirect refusal, exact stale-output parsing, and same-repository PR validation are material improvements. Findings 1, 5, and 6 remain controller trust/resource-boundary defects. ### Test / Documentation Gaps Add producer-shaped success-porcelain, Git-dir/object-dir escape, exact request-budget, repository-absence, output-flood, slow-response, outbound-size, and shared malformed-cursor cases. No documentation expansion is required. ### Follow-Up Candidates None. These defects belong to issue #9's exact receipt, bounded transport/reconciliation, absence, and shared-conformance criteria. ### Overall Recommendation REQUEST_CHANGES This recommendation applies only to frozen HEAD `416e81749916746cf41218538487ae28d19fc102`. Blocking findings: six. Forgejo Actions run #1963 reports success for the exact head, and `git diff --check origin/develop...HEAD` passed. I did not execute untrusted PR code in the reviewer context. Forgejo's workflow-job endpoint returned 404, so job logs were not independently inspected; live Forgejo integration remains unrun. The shared `kilo` identity authored the PR, so this review is intentionally persisted as `COMMENT`; it is not a formal Forgejo `REQUEST_CHANGES` state.
kilo left a comment

Architecture / Model-Correction Review

Frozen HEAD: 416e81749916746cf41218538487ae28d19fc102
Base: develop@8877b97b83629e942e1b8725dfd02bb70c658b27
Scope: issue #9, the whole PR, accepted ADR 0004/0005, docs/architecture/contracts.md, the threat model, and cycle-3 F11-F16. This is not a replacement full-PR verdict and is not a formal APPROVE.

Verdict

BOUNDED_CORRECTION

No material owner architecture, security, scope, or policy decision is required. Issue #9 already requires authorized conditional push, validated domain receipts, safe absence distinctions, bounded uncertain-operation reconciliation, and fake/reference conformance. ADR 0004 and the threat model already place credentials and enforcement in the trusted controller/OS boundary; ADR 0005 explicitly says repeated defects or remediation rounds do not themselves create a Human Gate.

Finding Disposition

No F11-F16 finding is invalid as a defect. Some overly local remediations would be invalid, as constrained below.

  • F11 untrusted object-store indirection: valid, but not solvable by canonicalizing an untrusted git rev-parse result alone. The current transport derives GIT_ALTERNATE_OBJECT_DIRECTORIES from workload-controlled Git metadata. The bounded correction is to stop discovering the credential-bearing push source through the workload worktree and require an explicit controller-owned, pinned object source whose trust/non-worker-writability is established by the existing Workspace/controller boundary. Locally validate the exact full OID exists and is a commit in that source, reject symlink/common-dir/alternates indirection outside the trusted source, and fail closed when the source cannot be established. The Forge adapter need not implement a new sandbox, provenance service, artifact store, or private-product integration; trusted-development mode may supply a controller-created bare repository, while hostile-worker operation remains unsupported until the accepted external sandbox/Workspace boundary supplies the source.
  • F12 zero-exit push acknowledgement: valid. A process exit code is transport evidence, not ADR 0003's validated domain receipt. Accept only one producer-shaped porcelain success record for the exact source OID and authorized destination ref, with no missing, extra, contradictory, or malformed update; otherwise return UNKNOWN. Keep exact structured stale parsing separate.
  • F13 reconciliation request-budget overrun: valid. Use one shared budget for every authenticated observation performed by reconciliation, including list, detail, base-branch, and head-branch reads. Check before each request. Exact-budget exhaustion returns UNKNOWN; it cannot return ACCEPTED or prove absence.
  • F14 repository identity absence: valid. Apply the existing explicit authoritative_absence(path) evidence rule to repository identity and make the fake represent repository ABSENT, INACCESSIBLE, and UNKNOWN distinctly. Real Forgejo 404 remains UNKNOWN when the transport cannot attest absence; blanket 404-to-ABSENT mapping would be invalid.
  • F15 uncapped Git output: valid. Replace unbounded capture_output=True with bounded concurrent stdout/stderr collection. On output-limit or timeout breach, terminate the process and return UNKNOWN, because the remote effect may already have occurred. Preserve enough bounded porcelain bytes for F12 validation.
  • F16 incomplete HTTP bounds: valid. Enforce the outbound encoded JSON limit before network I/O and a monotonic total operation deadline across connection/TLS, request transmission, response headers, bounded body read, and completion. A static per-socket timeout or a chunk loop that resets the same timeout is not a total deadline. Deadline expiry after possible transmission remains UNKNOWN; oversize input rejected before I/O is a local refusal, not a remote receipt.

Bounded Instructions

  1. Keep core provider-neutral, preserve adapters -> ports -> core, and expose no merge operation.
  2. Correct F11 at the adapter input plus accepted controller/Workspace trust seam. Do not claim that path ownership, resolve(), or a caller assertion alone proves hostile-worker isolation, and do not add a mandatory external service or private repository dependency.
  3. Make the concrete HTTP/Git transports enforce their advertised limits end to end. If a concrete mechanism cannot enforce a guarantee, fail it as unsupported/unknown rather than weakening the contract.
  4. Add producer-shaped adversarial tests for .git/common-dir/symlink/alternates escape, wrong-type or missing OID, exact and malformed success porcelain, extra update records, exact request-budget boundaries, repository absence classes, Git output floods, slow/trickling HTTP responses, and oversized outbound requests.
  5. Keep the existing same-repository PR scope and no-automatic-merge boundary. These corrections do not authorize a universal Git/Forge SDK, new provenance subsystem, custom sandbox, secrets manager, or product-runtime dependency.

Owner Decision

None. The trusted-source precondition and finite transport limits are consequences of already accepted architecture. If no trusted object source or truly bounded transport is available in a deployment, the correct existing decision is fail-closed unsupported operation, not an owner policy exception.

Exact-head Forgejo Actions run #1963 reports success. I did not execute PR code or tests; the workflow-job endpoint returned 404, so job logs were not independently inspected. This review is persisted as COMMENT only and does not approve or merge the PR.

## Architecture / Model-Correction Review **Frozen HEAD:** `416e81749916746cf41218538487ae28d19fc102` **Base:** `develop@8877b97b83629e942e1b8725dfd02bb70c658b27` **Scope:** issue #9, the whole PR, accepted ADR 0004/0005, `docs/architecture/contracts.md`, the threat model, and cycle-3 F11-F16. This is not a replacement full-PR verdict and is not a formal APPROVE. ### Verdict **BOUNDED_CORRECTION** No material owner architecture, security, scope, or policy decision is required. Issue #9 already requires authorized conditional push, validated domain receipts, safe absence distinctions, bounded uncertain-operation reconciliation, and fake/reference conformance. ADR 0004 and the threat model already place credentials and enforcement in the trusted controller/OS boundary; ADR 0005 explicitly says repeated defects or remediation rounds do not themselves create a Human Gate. ### Finding Disposition No F11-F16 finding is invalid as a defect. Some overly local remediations would be invalid, as constrained below. - **F11 untrusted object-store indirection: valid, but not solvable by canonicalizing an untrusted `git rev-parse` result alone.** The current transport derives `GIT_ALTERNATE_OBJECT_DIRECTORIES` from workload-controlled Git metadata. The bounded correction is to stop discovering the credential-bearing push source through the workload worktree and require an explicit controller-owned, pinned object source whose trust/non-worker-writability is established by the existing Workspace/controller boundary. Locally validate the exact full OID exists and is a commit in that source, reject symlink/common-dir/alternates indirection outside the trusted source, and fail closed when the source cannot be established. The Forge adapter need not implement a new sandbox, provenance service, artifact store, or private-product integration; trusted-development mode may supply a controller-created bare repository, while hostile-worker operation remains unsupported until the accepted external sandbox/Workspace boundary supplies the source. - **F12 zero-exit push acknowledgement: valid.** A process exit code is transport evidence, not ADR 0003's validated domain receipt. Accept only one producer-shaped porcelain success record for the exact source OID and authorized destination ref, with no missing, extra, contradictory, or malformed update; otherwise return `UNKNOWN`. Keep exact structured stale parsing separate. - **F13 reconciliation request-budget overrun: valid.** Use one shared budget for every authenticated observation performed by reconciliation, including list, detail, base-branch, and head-branch reads. Check before each request. Exact-budget exhaustion returns `UNKNOWN`; it cannot return `ACCEPTED` or prove absence. - **F14 repository identity absence: valid.** Apply the existing explicit `authoritative_absence(path)` evidence rule to repository identity and make the fake represent repository `ABSENT`, `INACCESSIBLE`, and `UNKNOWN` distinctly. Real Forgejo 404 remains `UNKNOWN` when the transport cannot attest absence; blanket 404-to-`ABSENT` mapping would be invalid. - **F15 uncapped Git output: valid.** Replace unbounded `capture_output=True` with bounded concurrent stdout/stderr collection. On output-limit or timeout breach, terminate the process and return `UNKNOWN`, because the remote effect may already have occurred. Preserve enough bounded porcelain bytes for F12 validation. - **F16 incomplete HTTP bounds: valid.** Enforce the outbound encoded JSON limit before network I/O and a monotonic total operation deadline across connection/TLS, request transmission, response headers, bounded body read, and completion. A static per-socket timeout or a chunk loop that resets the same timeout is not a total deadline. Deadline expiry after possible transmission remains `UNKNOWN`; oversize input rejected before I/O is a local refusal, not a remote receipt. ### Bounded Instructions 1. Keep core provider-neutral, preserve `adapters -> ports -> core`, and expose no merge operation. 2. Correct F11 at the adapter input plus accepted controller/Workspace trust seam. Do not claim that path ownership, `resolve()`, or a caller assertion alone proves hostile-worker isolation, and do not add a mandatory external service or private repository dependency. 3. Make the concrete HTTP/Git transports enforce their advertised limits end to end. If a concrete mechanism cannot enforce a guarantee, fail it as unsupported/unknown rather than weakening the contract. 4. Add producer-shaped adversarial tests for `.git`/common-dir/symlink/alternates escape, wrong-type or missing OID, exact and malformed success porcelain, extra update records, exact request-budget boundaries, repository absence classes, Git output floods, slow/trickling HTTP responses, and oversized outbound requests. 5. Keep the existing same-repository PR scope and no-automatic-merge boundary. These corrections do not authorize a universal Git/Forge SDK, new provenance subsystem, custom sandbox, secrets manager, or product-runtime dependency. ### Owner Decision None. The trusted-source precondition and finite transport limits are consequences of already accepted architecture. If no trusted object source or truly bounded transport is available in a deployment, the correct existing decision is fail-closed unsupported operation, not an owner policy exception. Exact-head Forgejo Actions run #1963 reports success. I did not execute PR code or tests; the workflow-job endpoint returned 404, so job logs were not independently inspected. This review is persisted as `COMMENT` only and does not approve or merge the PR.
fix: bound Forge transports and verify push receipts
All checks were successful
ci / check (pull_request) Successful in 22s
0a42123cf2
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: 0a42123cf2
Linked issue: #9

Summary

The cycle-3 remediation materially fixes the trusted object-source seam, exact success receipt, request-budget accounting, repository absence evidence, bounded Git output, and total HTTP deadline. Dependency direction remains adapters -> ports -> core, exact-head CI is green, and no merge capability is exposed. The current head still has blocking idempotency/replay defects and incomplete transport/resource guarantees.

Intent / Acceptance Evidence

Issue #9 criterion Evidence at frozen head Result
Fake and Forgejo share required-operation/receipt conformance Main suite is parametrized, but both adapters accept an invalid branch replay and the fixture uses revision values rejected by the concrete Git transport Partial
Identities remain opaque and qualified Core references remain qualified and Forgejo payload interpretation stays adapter-local Met
Pagination, permission-limited absence, stale, unsupported, and unknown remain distinct and safe Pagination and explicit 404 evidence improved; real stale porcelain and HTTP 401 are misclassified Not met
Only authorized exact-scope effects occur and receipts are domain-validated Success porcelain is now validated, but duplicate markers and invalid replays can still be accepted; resource bounds remain incomplete Not met
PR opening is tested while automatic merge is absent PR creation is covered and merge is not exposed Met, subject to reconciliation ambiguity below

Architecture / Information Flow

The change preserves adapters -> ports -> core, keeps provider payloads out of core, and does not add private/runtime dependencies. The remaining defects are at the exact-effect reconciliation and credential-bearing transport boundaries governed by ADR 0003, ADR 0005, docs/architecture/contracts.md, and the threat model.

Blocking Issues

  1. Blocking defect: PR reconciliation accepts one exact marker without proving the operation key is unique. src/creatidy_kernel/adapters/forgejo.py:369-398 returns ACCEPTED on the first exact matching PR and never examines the rest of that page or later pages. src/creatidy_kernel/adapters/fake_forge.py:334-358 has the same early return. If PR A exactly matches and PR B reuses the marker with different metadata/subjects, ordering A first hides the conflict and acknowledges an ambiguous duplicate effect. Scan the complete bounded search, return UNKNOWN for multiple/conflicting uses, and return UNKNOWN if the budget cannot prove uniqueness.

  2. Blocking defect: replay bypasses the branch-creation absence invariant. Effect.__post_init__ permits action="branch" with a non-null expected (src/creatidy_kernel/core/forge.py:99-109). Both apply() implementations route retries to reconciliation before enforcing expected is None (fake_forge.py:251-260, forgejo.py:251-261), and both reconcile paths accept when the target already equals the requested revision (fake_forge.py:320-330, forgejo.py:327-337). An authorized malformed branch effect with delivery_attempts=2 can therefore receive ACCEPTED even though branch creation was never a legal effect. Enforce action-specific invariants before dispatch/reconciliation and add the malformed replay to the shared suite.

  3. Security/privacy note and blocking defect: Git timeout/output-limit handling kills only the immediate process. run_git_bounded() starts no process group and calls process.kill() only on the parent PID (src/creatidy_kernel/adapters/forgejo_transport.py:128-159). git-remote-https, askpass, or another descendant can survive with credential-bearing environment/network activity after the adapter returns UNKNOWN. The test at tests/test_forge_transports.py:238-249 uses childless processes and cannot detect this. Start an isolated process group/session and terminate/reap the entire group on every exceptional path.

  4. Blocking stale-semantics defect: producer-shaped stale porcelain is rejected by the parser. Success parsing correctly expects To <remote>, one status line, and Done, but stale parsing at forgejo_transport.py:326-327 accepts only the unframed status line. Real git push --porcelain --force-with-lease rejection output uses the same three-line framing, so an ordinary lease race becomes UNKNOWN, not STALE. tests/test_forge_transports.py:114-130 codifies the synthetic one-line form. Parse the exact framed single-ref rejection while continuing to reject misleading stderr/extra records.

  5. Blocking HTTP boundary defect: the outbound limit is post-allocation and its refusal escapes the Forge receipt boundary. forgejo_transport.py:88-95 fully materializes the JSON string and encoded bytes before checking max_bytes, so a large worker-derived PR body can consume unbounded memory/CPU before rejection. The resulting ValueError is not caught by ForgejoForge.apply() at forgejo.py:294-303, so an otherwise valid authorized effect escapes without a normalized receipt or explicit supported-domain exception. Bound serialization before full allocation and normalize the pre-I/O refusal.

  6. Blocking boundedness defect: trusted-source validation recursively walks the whole object database without a budget. forgejo_transport.py:202-204 walks the entire bare repository at construction and lines 262-278 repeat that traversal before every push, outside the subprocess timeout and without entry/depth/time limits. A large object store can therefore consume unbounded controller time and filesystem work despite the transport's finite-limit contract. Replace the full walk with a bounded/pinned validation strategy or enforce an explicit traversal budget that fails closed.

  7. Blocking observation gap: authentication failures are collapsed into generic uncertainty. ForgejoForge.identity(), branch(), and change() treat only HTTP 403 as INACCESSIBLE (forgejo.py:104-155). Forgejo 401 responses for missing/invalid/expired credentials become UNKNOWN, despite being explicit authentication/permission evidence. Handle producer-shaped 401 as INACCESSIBLE and add shared/reference cases distinct from authoritative absence and transport uncertainty.

Non-blocking Suggestions

None beyond the required remediation.

Questions for the Author

None.

Security / Privacy Notes

The controller-owned bare source, disabled ambient Git configuration/hooks, exact one-ref success parser, redirect refusal, bounded captured bytes, and signal-backed main-thread HTTP deadline are material improvements. The surviving process-tree and unbounded local-allocation/traversal issues remain controller resource and credential-boundary defects.

Test / Documentation Gaps

Add duplicate-marker ordering/across-page cases, malformed branch replay, a descendant-process timeout/flood fixture, producer-shaped framed stale output, pre-allocation outbound limits, traversal-budget exhaustion, and HTTP 401 classifications. Use production-valid 40/64-character object IDs in shared Forgejo operation fixtures and cover malformed same-provider revisions. No documentation expansion is required.

Follow-Up Candidates

None. These findings belong to issue #9's required conformance, exact-effect reconciliation, distinct stale/inaccessible semantics, normalized receipts, and bounded transport behavior.

Overall Recommendation

REQUEST_CHANGES

This recommendation applies only to frozen HEAD 0a42123cf23a9cd3d03a9637290375ef09aa31e7. Blocking findings: seven. Forgejo Actions run #1964 reports success for the exact head, and git diff --check origin/develop...HEAD passed. I did not execute untrusted PR code or tests in the reviewer context. Forgejo's workflow-job and artifact endpoints returned 404, so logs/artifacts were not independently inspected; live Forgejo integration remains unrun. The authenticated kilo identity authored the PR, so this review is intentionally persisted as COMMENT; it is not a formal Forgejo REQUEST_CHANGES state.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** issue-9-forge-port -> develop **Frozen head:** 0a42123cf23a9cd3d03a9637290375ef09aa31e7 **Linked issue:** #9 ### Summary The cycle-3 remediation materially fixes the trusted object-source seam, exact success receipt, request-budget accounting, repository absence evidence, bounded Git output, and total HTTP deadline. Dependency direction remains `adapters -> ports -> core`, exact-head CI is green, and no merge capability is exposed. The current head still has blocking idempotency/replay defects and incomplete transport/resource guarantees. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence at frozen head | Result | | --- | --- | --- | | Fake and Forgejo share required-operation/receipt conformance | Main suite is parametrized, but both adapters accept an invalid branch replay and the fixture uses revision values rejected by the concrete Git transport | Partial | | Identities remain opaque and qualified | Core references remain qualified and Forgejo payload interpretation stays adapter-local | Met | | Pagination, permission-limited absence, stale, unsupported, and unknown remain distinct and safe | Pagination and explicit 404 evidence improved; real stale porcelain and HTTP 401 are misclassified | Not met | | Only authorized exact-scope effects occur and receipts are domain-validated | Success porcelain is now validated, but duplicate markers and invalid replays can still be accepted; resource bounds remain incomplete | Not met | | PR opening is tested while automatic merge is absent | PR creation is covered and merge is not exposed | Met, subject to reconciliation ambiguity below | ### Architecture / Information Flow The change preserves `adapters -> ports -> core`, keeps provider payloads out of core, and does not add private/runtime dependencies. The remaining defects are at the exact-effect reconciliation and credential-bearing transport boundaries governed by ADR 0003, ADR 0005, `docs/architecture/contracts.md`, and the threat model. ### Blocking Issues 1. **Blocking defect: PR reconciliation accepts one exact marker without proving the operation key is unique.** `src/creatidy_kernel/adapters/forgejo.py:369-398` returns `ACCEPTED` on the first exact matching PR and never examines the rest of that page or later pages. `src/creatidy_kernel/adapters/fake_forge.py:334-358` has the same early return. If PR A exactly matches and PR B reuses the marker with different metadata/subjects, ordering A first hides the conflict and acknowledges an ambiguous duplicate effect. Scan the complete bounded search, return `UNKNOWN` for multiple/conflicting uses, and return `UNKNOWN` if the budget cannot prove uniqueness. 2. **Blocking defect: replay bypasses the branch-creation absence invariant.** `Effect.__post_init__` permits `action="branch"` with a non-null `expected` (`src/creatidy_kernel/core/forge.py:99-109`). Both `apply()` implementations route retries to reconciliation before enforcing `expected is None` (`fake_forge.py:251-260`, `forgejo.py:251-261`), and both reconcile paths accept when the target already equals the requested revision (`fake_forge.py:320-330`, `forgejo.py:327-337`). An authorized malformed branch effect with `delivery_attempts=2` can therefore receive `ACCEPTED` even though branch creation was never a legal effect. Enforce action-specific invariants before dispatch/reconciliation and add the malformed replay to the shared suite. 3. **Security/privacy note and blocking defect: Git timeout/output-limit handling kills only the immediate process.** `run_git_bounded()` starts no process group and calls `process.kill()` only on the parent PID (`src/creatidy_kernel/adapters/forgejo_transport.py:128-159`). `git-remote-https`, askpass, or another descendant can survive with credential-bearing environment/network activity after the adapter returns `UNKNOWN`. The test at `tests/test_forge_transports.py:238-249` uses childless processes and cannot detect this. Start an isolated process group/session and terminate/reap the entire group on every exceptional path. 4. **Blocking stale-semantics defect: producer-shaped stale porcelain is rejected by the parser.** Success parsing correctly expects `To <remote>`, one status line, and `Done`, but stale parsing at `forgejo_transport.py:326-327` accepts only the unframed status line. Real `git push --porcelain --force-with-lease` rejection output uses the same three-line framing, so an ordinary lease race becomes `UNKNOWN`, not `STALE`. `tests/test_forge_transports.py:114-130` codifies the synthetic one-line form. Parse the exact framed single-ref rejection while continuing to reject misleading stderr/extra records. 5. **Blocking HTTP boundary defect: the outbound limit is post-allocation and its refusal escapes the Forge receipt boundary.** `forgejo_transport.py:88-95` fully materializes the JSON string and encoded bytes before checking `max_bytes`, so a large worker-derived PR body can consume unbounded memory/CPU before rejection. The resulting `ValueError` is not caught by `ForgejoForge.apply()` at `forgejo.py:294-303`, so an otherwise valid authorized effect escapes without a normalized receipt or explicit supported-domain exception. Bound serialization before full allocation and normalize the pre-I/O refusal. 6. **Blocking boundedness defect: trusted-source validation recursively walks the whole object database without a budget.** `forgejo_transport.py:202-204` walks the entire bare repository at construction and lines 262-278 repeat that traversal before every push, outside the subprocess timeout and without entry/depth/time limits. A large object store can therefore consume unbounded controller time and filesystem work despite the transport's finite-limit contract. Replace the full walk with a bounded/pinned validation strategy or enforce an explicit traversal budget that fails closed. 7. **Blocking observation gap: authentication failures are collapsed into generic uncertainty.** `ForgejoForge.identity()`, `branch()`, and `change()` treat only HTTP 403 as `INACCESSIBLE` (`forgejo.py:104-155`). Forgejo 401 responses for missing/invalid/expired credentials become `UNKNOWN`, despite being explicit authentication/permission evidence. Handle producer-shaped 401 as `INACCESSIBLE` and add shared/reference cases distinct from authoritative absence and transport uncertainty. ### Non-blocking Suggestions None beyond the required remediation. ### Questions for the Author None. ### Security / Privacy Notes The controller-owned bare source, disabled ambient Git configuration/hooks, exact one-ref success parser, redirect refusal, bounded captured bytes, and signal-backed main-thread HTTP deadline are material improvements. The surviving process-tree and unbounded local-allocation/traversal issues remain controller resource and credential-boundary defects. ### Test / Documentation Gaps Add duplicate-marker ordering/across-page cases, malformed branch replay, a descendant-process timeout/flood fixture, producer-shaped framed stale output, pre-allocation outbound limits, traversal-budget exhaustion, and HTTP 401 classifications. Use production-valid 40/64-character object IDs in shared Forgejo operation fixtures and cover malformed same-provider revisions. No documentation expansion is required. ### Follow-Up Candidates None. These findings belong to issue #9's required conformance, exact-effect reconciliation, distinct stale/inaccessible semantics, normalized receipts, and bounded transport behavior. ### Overall Recommendation REQUEST_CHANGES This recommendation applies only to frozen HEAD `0a42123cf23a9cd3d03a9637290375ef09aa31e7`. Blocking findings: seven. Forgejo Actions run #1964 reports success for the exact head, and `git diff --check origin/develop...HEAD` passed. I did not execute untrusted PR code or tests in the reviewer context. Forgejo's workflow-job and artifact endpoints returned 404, so logs/artifacts were not independently inspected; live Forgejo integration remains unrun. The authenticated `kilo` identity authored the PR, so this review is intentionally persisted as `COMMENT`; it is not a formal Forgejo `REQUEST_CHANGES` state.
kilo left a comment

Architecture / Model-Correction Review

Frozen HEAD: 0a42123cf23a9cd3d03a9637290375ef09aa31e7
Base: develop@8877b97b83629e942e1b8725dfd02bb70c658b27
Scope: issue #9, the complete PR, accepted ADR 0004/0005, docs/architecture/contracts.md, the threat model, and cycle-4 F17-F23. This is not a full PR approval and is not a merge recommendation.

Verdict

BOUNDED_CORRECTION

The current shape should be boundedly simplified around its existing seams. No material owner authority, architecture, security, scope, or policy decision is required. Issue #9 and the accepted architecture already require canonical effect identity, replay-safe exact-effect reconciliation, normalized producer receipts, finite transport/resource behavior, and inaccessible/absent/unknown distinctions. ADR 0005 explicitly rejects remediation count as a Human Gate by itself.

Finding Disposition

  • F17 duplicate marker uniqueness: valid. First-match acceptance is unsafe. Complete the bounded scan, retain at most one marker-bearing candidate, and return UNKNOWN for any duplicate/conflict or incomplete/budget-exhausted scan. Accept only one exact candidate after authoritative scan completion and exact current head/base checks. Apply the same cardinality rule in the fake.
  • F18 branch replay invariant: valid in a narrower form. branch with non-null expected is an invalid effect shape that replay currently bypasses. Enforce branch.expected is None before first delivery or reconciliation, preferably in Effect.__post_init__. It would be invalid scope expansion to require forensic proof of historical absence for every legitimate lost-reply branch creation; exact stable-key/postcondition reconciliation remains allowed.
  • F19 descendant process cleanup: valid. Run each bounded Git command in a new process session/group and terminate/reap the group on timeout, output overflow, or exceptional exit. This is process lifecycle containment, not a new sandbox claim.
  • F20 stale porcelain framing: valid. Parse STALE only from the exact producer-shaped three-line porcelain frame for the configured destination and single authorized ref. Malformed, extra, contradictory, or stderr-only evidence remains UNKNOWN.
  • F21 outbound request bound/receipt: valid, bounded to the adapter edge. Reject the adapter-owned flat PR request shape before accumulating more than the configured serialized limit, expose a specific deterministic local refusal, and normalize it to REJECTED without network I/O. Network/deadline ambiguity remains UNKNOWN. Do not add a provider-specific arbitrary body limit to core or catch unrelated ValueErrors broadly.
  • F22 recursive source walk: valid as a boundedness defect; stronger security framing is invalid. Remove the unbounded recursive os.walk. Retain finite checks for the explicit controller-owned bare source, pinned top-level identity, commondir/alternates indirection, exact repository binding, and commit OID/type. An exhaustive path walk cannot establish hostile-worker isolation and must not grow into a provenance service or filesystem verifier.
  • F23 HTTP 401 classification: valid. Normalize both 401 and 403 to INACCESSIBLE; preserve explicit authoritative 404 as ABSENT, concealed/non-authoritative 404 as UNKNOWN, and malformed/transport failures as UNKNOWN.

No F17-F23 finding is wholly invalid. F18 and F22 are overstated if interpreted as demands for historical-absence proof or hostile-source certification; those interpretations should not drive remediation.

One Coherent Minimal Correction

  1. Canonicalize effect validity once: put action-specific invariants in the core effect model before delivery-attempt dispatch, so first delivery and replay cannot diverge.
  2. Make reconciliation evidence-aggregating: replace early return with a bounded full-scan/cardinality decision; exactly one complete exact match may be accepted, while duplicate, conflicting, malformed, repeated-cursor, or incomplete evidence stays UNKNOWN.
  3. Simplify and bound the concrete transports: one exact porcelain parser for framed success/stale outcomes, one process-group lifecycle for Git, bounded pre-I/O PR serialization, and finite targeted validation of the trusted bare source instead of recursive pseudo-sandboxing.
  4. Complete the existing observation matrix: 401/403 inaccessible, attested 404 absent, all other uncertain cases unknown; add focused producer-shaped tests for each correction.

The public Forge protocol and existing Effect, Receipt, Page, and Presence concepts are sufficient. Preserve same-repository PR scope, no merge operation, no fork-universal behavior, adapters -> ports -> core, and no new service/private dependency.

Evidence Boundary

The frozen head and base were reverified immediately before this review. Forgejo reports exact-head Actions run #1964 successful and the PR reports make check/make package-check passing, but those results do not negate the model defects. I did not execute untrusted PR code, edit the branch, or merge. This review is persisted as COMMENT only and never constitutes full PR approval.

## Architecture / Model-Correction Review **Frozen HEAD:** `0a42123cf23a9cd3d03a9637290375ef09aa31e7` **Base:** `develop@8877b97b83629e942e1b8725dfd02bb70c658b27` **Scope:** issue #9, the complete PR, accepted ADR 0004/0005, `docs/architecture/contracts.md`, the threat model, and cycle-4 F17-F23. This is not a full PR approval and is not a merge recommendation. ### Verdict **BOUNDED_CORRECTION** The current shape should be boundedly simplified around its existing seams. No material owner authority, architecture, security, scope, or policy decision is required. Issue #9 and the accepted architecture already require canonical effect identity, replay-safe exact-effect reconciliation, normalized producer receipts, finite transport/resource behavior, and inaccessible/absent/unknown distinctions. ADR 0005 explicitly rejects remediation count as a Human Gate by itself. ### Finding Disposition - **F17 duplicate marker uniqueness: valid.** First-match acceptance is unsafe. Complete the bounded scan, retain at most one marker-bearing candidate, and return `UNKNOWN` for any duplicate/conflict or incomplete/budget-exhausted scan. Accept only one exact candidate after authoritative scan completion and exact current head/base checks. Apply the same cardinality rule in the fake. - **F18 branch replay invariant: valid in a narrower form.** `branch` with non-null `expected` is an invalid effect shape that replay currently bypasses. Enforce `branch.expected is None` before first delivery or reconciliation, preferably in `Effect.__post_init__`. It would be invalid scope expansion to require forensic proof of historical absence for every legitimate lost-reply branch creation; exact stable-key/postcondition reconciliation remains allowed. - **F19 descendant process cleanup: valid.** Run each bounded Git command in a new process session/group and terminate/reap the group on timeout, output overflow, or exceptional exit. This is process lifecycle containment, not a new sandbox claim. - **F20 stale porcelain framing: valid.** Parse `STALE` only from the exact producer-shaped three-line porcelain frame for the configured destination and single authorized ref. Malformed, extra, contradictory, or stderr-only evidence remains `UNKNOWN`. - **F21 outbound request bound/receipt: valid, bounded to the adapter edge.** Reject the adapter-owned flat PR request shape before accumulating more than the configured serialized limit, expose a specific deterministic local refusal, and normalize it to `REJECTED` without network I/O. Network/deadline ambiguity remains `UNKNOWN`. Do not add a provider-specific arbitrary body limit to core or catch unrelated `ValueError`s broadly. - **F22 recursive source walk: valid as a boundedness defect; stronger security framing is invalid.** Remove the unbounded recursive `os.walk`. Retain finite checks for the explicit controller-owned bare source, pinned top-level identity, `commondir`/alternates indirection, exact repository binding, and commit OID/type. An exhaustive path walk cannot establish hostile-worker isolation and must not grow into a provenance service or filesystem verifier. - **F23 HTTP 401 classification: valid.** Normalize both 401 and 403 to `INACCESSIBLE`; preserve explicit authoritative 404 as `ABSENT`, concealed/non-authoritative 404 as `UNKNOWN`, and malformed/transport failures as `UNKNOWN`. No F17-F23 finding is wholly invalid. F18 and F22 are overstated if interpreted as demands for historical-absence proof or hostile-source certification; those interpretations should not drive remediation. ### One Coherent Minimal Correction 1. **Canonicalize effect validity once:** put action-specific invariants in the core effect model before delivery-attempt dispatch, so first delivery and replay cannot diverge. 2. **Make reconciliation evidence-aggregating:** replace early return with a bounded full-scan/cardinality decision; exactly one complete exact match may be accepted, while duplicate, conflicting, malformed, repeated-cursor, or incomplete evidence stays `UNKNOWN`. 3. **Simplify and bound the concrete transports:** one exact porcelain parser for framed success/stale outcomes, one process-group lifecycle for Git, bounded pre-I/O PR serialization, and finite targeted validation of the trusted bare source instead of recursive pseudo-sandboxing. 4. **Complete the existing observation matrix:** 401/403 inaccessible, attested 404 absent, all other uncertain cases unknown; add focused producer-shaped tests for each correction. The public `Forge` protocol and existing `Effect`, `Receipt`, `Page`, and `Presence` concepts are sufficient. Preserve same-repository PR scope, no merge operation, no fork-universal behavior, `adapters -> ports -> core`, and no new service/private dependency. ### Evidence Boundary The frozen head and base were reverified immediately before this review. Forgejo reports exact-head Actions run #1964 successful and the PR reports `make check`/`make package-check` passing, but those results do not negate the model defects. I did not execute untrusted PR code, edit the branch, or merge. This review is persisted as `COMMENT` only and never constitutes full PR approval.
fix: reconcile unique Forge effects and bound replay
All checks were successful
ci / check (pull_request) Successful in 26s
48ec096862
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: 48ec096862
Linked issue: #9

Summary

The cycle-4 remediation fixes the previously reported branch-effect shape, duplicate-marker scan cardinality on a static result set, framed stale parsing, local request refusal, HTTP 401 classification, recursive source walk, and timeout/output-overflow process-group cleanup. Dependency direction remains adapters -> ports -> core, exact-head CI is green, and no merge capability is exposed. Three blocking defects remain at the exact-effect and hostile/uncertain transport boundaries.

Intent / Acceptance Evidence

Issue #9 criterion Evidence at frozen head Result
Fake and Forgejo share required-operation/receipt conformance Shared tests cover static duplicate ordering/pages and effect-shape validation; they do not model mutable offset pagination or nonzero-exit descendants Partial
Identities remain opaque and qualified Core references remain provider-qualified and Forgejo payload parsing stays adapter-local Met
Pagination, permission-limited absence, stale, unsupported, and unknown effects remain distinct and safe 401/403, explicit absence and framed stale handling are corrected; concurrent pagination and no-op CAS evidence remain unsafe Not met
Only authorized exact-scope effects occur; transport success is not acknowledgement Authorization is explicit, but an up-to-date porcelain record can acknowledge an unmet expected-old/absence precondition Not met
PR opening is tested while automatic merge is absent PR creation/replay is covered and no merge operation is exposed Met, subject to reconciliation uniqueness below

Architecture / Information Flow

The change preserves the accepted inward dependency boundary and keeps Forgejo payloads outside core. The remaining findings are confined to the reference adapter/transport and are required by ADR 0003, ADR 0005, docs/architecture/contracts.md, and the threat model; they require no new service, merge capability, or architecture decision.

Blocking Issues

  1. Blocking exact-effect defect: up to date can bypass the expected-old or absent-target precondition. src/creatidy_kernel/adapters/forgejo_transport.py:321-337 accepts =\t<sha>:<ref>\t[up to date] as ACCEPTED. Git can report this no-op when the remote ref already equals the requested revision without proving that --force-with-lease=<ref>:<expected> matched; for branch creation it likewise does not prove the target was absent. A first delivery can therefore acknowledge an operation whose required CAS precondition never held. Treat an up-to-date first attempt as UNKNOWN unless the exact precondition can be proven independently; add stale-expected and pre-existing-same-revision branch fixtures.

  2. Security/privacy note and blocking process-lifecycle defect: a normal nonzero Git exit can leave credential-bearing descendants alive. run_git_bounded() creates a new session at src/creatidy_kernel/adapters/forgejo_transport.py:145-147, but kills the process group only from the exception path at lines 170-176. If Git exits nonzero after a descendant closes its inherited output descriptors, collection completes normally and lines 177-182 return without terminating that descendant. Existing descendant tests at tests/test_forge_transports.py:281-294 cover only timeout/output overflow. Reap/terminate the command's process group on every completed non-success path and add a prompt-parent-failure fixture whose child closes stdout/stderr before waiting.

  3. Blocking reconciliation race: offset pagination does not prove marker uniqueness on a changing PR list. _page() advances numeric pages until an empty result (src/creatidy_kernel/adapters/forgejo.py:184-230), and reconcile() treats that traversal as a complete uniqueness proof before returning ACCEPTED (src/creatidy_kernel/adapters/forgejo.py:337-412). Forgejo's PR list is mutable: if a conflicting marker-bearing PR is inserted ahead of already-read pages, page offsets shift and the new PR can remain entirely behind the cursor while the later empty page still marks the scan complete. The static ordering/across-page fixtures at tests/test_forge.py:374-395 cannot expose this. Use a stable high-water/cursor strategy or require a bounded stable rescan; any mutation, incomplete stability proof, or exhausted budget must remain UNKNOWN.

Non-blocking Suggestions

None beyond the blocking remediation.

Questions for the Author

None.

Security / Privacy Notes

HTTPS-only origin binding, redirect refusal, total HTTP deadline, request/response/output bounds, controlled Git configuration, explicit controller-owned bare source, exact framed stale parsing, and timeout/overflow group cleanup are material improvements. Finding 2 remains a credential/process containment gap.

Test / Documentation Gaps

Add producer-shaped cases for pre-existing same-revision targets with mismatched lease/required absence, nonzero parent exit with a detached-output descendant in the same process group, and a duplicate marker inserted ahead of the active pagination cursor. No documentation expansion is required.

Follow-Up Candidates

None. These defects belong to issue #9's exact authorized effect, uncertainty reconciliation, pagination, and bounded transport acceptance criteria.

Overall Recommendation

REQUEST_CHANGES

This recommendation applies only to frozen HEAD 48ec0968628739430be04f62e08db903664f1b9f. Blocking findings: three. Forgejo Actions run #1965 reports success for the exact head; the PR reports make check with 274 passing tests and make package-check passing. I did not execute untrusted PR code in the reviewer context. Forgejo's workflow-job and artifact endpoints returned 404, so logs/artifacts were not independently inspected; live Forgejo integration remains unrun. The authenticated kilo identity authored the PR, so this review is intentionally persisted as COMMENT; it is not a formal Forgejo REQUEST_CHANGES state.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** issue-9-forge-port -> develop **Frozen head:** 48ec0968628739430be04f62e08db903664f1b9f **Linked issue:** #9 ### Summary The cycle-4 remediation fixes the previously reported branch-effect shape, duplicate-marker scan cardinality on a static result set, framed stale parsing, local request refusal, HTTP 401 classification, recursive source walk, and timeout/output-overflow process-group cleanup. Dependency direction remains `adapters -> ports -> core`, exact-head CI is green, and no merge capability is exposed. Three blocking defects remain at the exact-effect and hostile/uncertain transport boundaries. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence at frozen head | Result | | --- | --- | --- | | Fake and Forgejo share required-operation/receipt conformance | Shared tests cover static duplicate ordering/pages and effect-shape validation; they do not model mutable offset pagination or nonzero-exit descendants | Partial | | Identities remain opaque and qualified | Core references remain provider-qualified and Forgejo payload parsing stays adapter-local | Met | | Pagination, permission-limited absence, stale, unsupported, and unknown effects remain distinct and safe | 401/403, explicit absence and framed stale handling are corrected; concurrent pagination and no-op CAS evidence remain unsafe | Not met | | Only authorized exact-scope effects occur; transport success is not acknowledgement | Authorization is explicit, but an up-to-date porcelain record can acknowledge an unmet expected-old/absence precondition | Not met | | PR opening is tested while automatic merge is absent | PR creation/replay is covered and no merge operation is exposed | Met, subject to reconciliation uniqueness below | ### Architecture / Information Flow The change preserves the accepted inward dependency boundary and keeps Forgejo payloads outside core. The remaining findings are confined to the reference adapter/transport and are required by ADR 0003, ADR 0005, `docs/architecture/contracts.md`, and the threat model; they require no new service, merge capability, or architecture decision. ### Blocking Issues 1. **Blocking exact-effect defect: `up to date` can bypass the expected-old or absent-target precondition.** `src/creatidy_kernel/adapters/forgejo_transport.py:321-337` accepts `=\t<sha>:<ref>\t[up to date]` as `ACCEPTED`. Git can report this no-op when the remote ref already equals the requested revision without proving that `--force-with-lease=<ref>:<expected>` matched; for branch creation it likewise does not prove the target was absent. A first delivery can therefore acknowledge an operation whose required CAS precondition never held. Treat an up-to-date first attempt as `UNKNOWN` unless the exact precondition can be proven independently; add stale-expected and pre-existing-same-revision branch fixtures. 2. **Security/privacy note and blocking process-lifecycle defect: a normal nonzero Git exit can leave credential-bearing descendants alive.** `run_git_bounded()` creates a new session at `src/creatidy_kernel/adapters/forgejo_transport.py:145-147`, but kills the process group only from the exception path at lines 170-176. If Git exits nonzero after a descendant closes its inherited output descriptors, collection completes normally and lines 177-182 return without terminating that descendant. Existing descendant tests at `tests/test_forge_transports.py:281-294` cover only timeout/output overflow. Reap/terminate the command's process group on every completed non-success path and add a prompt-parent-failure fixture whose child closes stdout/stderr before waiting. 3. **Blocking reconciliation race: offset pagination does not prove marker uniqueness on a changing PR list.** `_page()` advances numeric pages until an empty result (`src/creatidy_kernel/adapters/forgejo.py:184-230`), and `reconcile()` treats that traversal as a complete uniqueness proof before returning `ACCEPTED` (`src/creatidy_kernel/adapters/forgejo.py:337-412`). Forgejo's PR list is mutable: if a conflicting marker-bearing PR is inserted ahead of already-read pages, page offsets shift and the new PR can remain entirely behind the cursor while the later empty page still marks the scan complete. The static ordering/across-page fixtures at `tests/test_forge.py:374-395` cannot expose this. Use a stable high-water/cursor strategy or require a bounded stable rescan; any mutation, incomplete stability proof, or exhausted budget must remain `UNKNOWN`. ### Non-blocking Suggestions None beyond the blocking remediation. ### Questions for the Author None. ### Security / Privacy Notes HTTPS-only origin binding, redirect refusal, total HTTP deadline, request/response/output bounds, controlled Git configuration, explicit controller-owned bare source, exact framed stale parsing, and timeout/overflow group cleanup are material improvements. Finding 2 remains a credential/process containment gap. ### Test / Documentation Gaps Add producer-shaped cases for pre-existing same-revision targets with mismatched lease/required absence, nonzero parent exit with a detached-output descendant in the same process group, and a duplicate marker inserted ahead of the active pagination cursor. No documentation expansion is required. ### Follow-Up Candidates None. These defects belong to issue #9's exact authorized effect, uncertainty reconciliation, pagination, and bounded transport acceptance criteria. ### Overall Recommendation REQUEST_CHANGES This recommendation applies only to frozen HEAD `48ec0968628739430be04f62e08db903664f1b9f`. Blocking findings: three. Forgejo Actions run #1965 reports success for the exact head; the PR reports `make check` with 274 passing tests and `make package-check` passing. I did not execute untrusted PR code in the reviewer context. Forgejo's workflow-job and artifact endpoints returned 404, so logs/artifacts were not independently inspected; live Forgejo integration remains unrun. The authenticated `kilo` identity authored the PR, so this review is intentionally persisted as `COMMENT`; it is not a formal Forgejo `REQUEST_CHANGES` state.
fix: require known receipts for uncertain Forge replay
All checks were successful
ci / check (pull_request) Successful in 28s
b35619fa27
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: b35619fa2754b87fa44a9642665de30c99513741
Linked issue: #9

Summary

The change establishes the intended inward dependency direction, an opaque Forge port, an independent fake, bounded HTTPS/Git transports, conditional effects, and no merge capability. Three blocking correctness/conformance defects remain at this exact head.

Intent / Acceptance Evidence

Issue #9 criterion Evidence Result
Shared fake/Forgejo conformance Shared parametrized suite covers observations, effects, uncertainty, absence, and reconciliation, but misses malformed same-provider revisions and oversized numeric cursors. Partial
Opaque qualified identities Reference stays qualified and payload parsing is adapter-local, but Forgejo Git revision payloads are not validated before acceptance. Partial
Distinct safe absence/stale/unsupported/unknown outcomes Permission/absence, empty-page completion, stale push, and replay uncertainty are modeled; unsupported handling is unreachable and the fake can raise on oversized cursors. Not met
Only exact authorized effects; transport success is insufficient Authorization, expected-old push, exact PR metadata, repository identity, and known-reference replay exist; malformed revisions can still support an accepted receipt. Not met
PR creation tested; no automatic merge Covered, and no merge action/capability is exposed. Met

Architecture / Information Flow

The dependency direction remains adapters -> ports -> core. Core does not depend on Forgejo, private repositories, cloud services, or live infrastructure. The concrete transports are controller-facing and bounded, but adapter validation must close the malformed-revision trust boundary before remote evidence can become domain acceptance.

Blocking Issues

  1. Malformed Forgejo revision identities can be accepted as exact domain evidence. src/creatidy_kernel/adapters/forgejo.py:86-90 validates only the forgejo: prefix. Branch and PR observations accept any nonempty revision string at forgejo.py:128-138 and forgejo.py:160-178; PR creation can then return ACCEPTED at forgejo.py:283-322 when those malformed values match the effect. The fake repeats the weak validation at src/creatidy_kernel/adapters/fake_forge.py:163-180 and fake_forge.py:237-249, while forgejo_transport.py:258-265 correctly requires a full 40- or 64-character hexadecimal object ID. Validate revision payloads using that full-OID grammar before producing observations or receipts, with shared malformed-revision coverage.

  2. Unsupported capabilities cannot be represented or rejected explicitly. ForgejoForge.capabilities() always advertises all operations at src/creatidy_kernel/adapters/forgejo.py:75-76; the UnsupportedForge checks at forgejo.py:244-256 test that same constant set and are unreachable. FakeForge is likewise fixed at src/creatidy_kernel/adapters/fake_forge.py:123-124. Configurations without PR or conditional-push support therefore advertise support and degrade into generic rejection/unknown outcomes. Derive capabilities from configured/probed transport support and cover supported/unsupported fixtures in the shared suite.

  3. Oversized numeric cursors violate fake/reference conformance. ForgejoForge._page() catches integer conversion failure at src/creatidy_kernel/adapters/forgejo.py:180-186, while FakeForge._page() performs unguarded int(cursor) at src/creatidy_kernel/adapters/fake_forge.py:182-192. Python rejects decimal strings beyond its configured digit limit, so Forgejo fails closed but the fake raises. The same issue exists for known PR references at fake_forge.py:326-337. Bound input length or catch conversion failures consistently, and add long numeric cursor/reference cases.

Non-blocking Suggestions

None beyond the direct issue work above.

Questions for the Author

None.

Security / Privacy Notes

No merge operation, unconditional push, redirects, ambient credential helper, worker-controlled Git hooks, secrets, or private-runtime dependency is introduced. Git and HTTP operations have finite bounds and controlled environments. Blocking issue 1 remains a trust-boundary defect because malformed remote responses can be elevated into accepted domain evidence.

Test / Documentation Gaps

  • Add shared malformed same-provider revision cases for branch observations, PR creation responses, authorized effects, and reconciliation.
  • Add explicit supported/unsupported capability fixtures.
  • Add oversized numeric cursor and change-reference cases.
  • No live Forgejo integration was run; this is residual validation rather than a substitute for the offline suite.
  • README.md:9-18 still says the package does not contact providers or perform live effects, although this PR adds concrete HTTPS and credential-bearing Git transports. Distinguish not-autonomously-invoked from not-implemented.
  • README.md:54-56 still describes the Forge port as future integration.

Follow-Up Candidates

None. These defects and documentation corrections are directly within issue #9.

Overall Recommendation

REQUEST_CHANGES for exact frozen HEAD b35619fa2754b87fa44a9642665de30c99513741.

The authenticated Forgejo account (kilo) is also the PR author, so this substantive request-changes verdict is persisted as COMMENT; it is not a formal Forgejo REQUEST_CHANGES review.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** issue-9-forge-port -> develop **Frozen head:** `b35619fa2754b87fa44a9642665de30c99513741` **Linked issue:** #9 ### Summary The change establishes the intended inward dependency direction, an opaque Forge port, an independent fake, bounded HTTPS/Git transports, conditional effects, and no merge capability. Three blocking correctness/conformance defects remain at this exact head. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence | Result | |---|---|---| | Shared fake/Forgejo conformance | Shared parametrized suite covers observations, effects, uncertainty, absence, and reconciliation, but misses malformed same-provider revisions and oversized numeric cursors. | Partial | | Opaque qualified identities | `Reference` stays qualified and payload parsing is adapter-local, but Forgejo Git revision payloads are not validated before acceptance. | Partial | | Distinct safe absence/stale/unsupported/unknown outcomes | Permission/absence, empty-page completion, stale push, and replay uncertainty are modeled; unsupported handling is unreachable and the fake can raise on oversized cursors. | Not met | | Only exact authorized effects; transport success is insufficient | Authorization, expected-old push, exact PR metadata, repository identity, and known-reference replay exist; malformed revisions can still support an accepted receipt. | Not met | | PR creation tested; no automatic merge | Covered, and no merge action/capability is exposed. | Met | ### Architecture / Information Flow The dependency direction remains `adapters -> ports -> core`. Core does not depend on Forgejo, private repositories, cloud services, or live infrastructure. The concrete transports are controller-facing and bounded, but adapter validation must close the malformed-revision trust boundary before remote evidence can become domain acceptance. ### Blocking Issues 1. **Malformed Forgejo revision identities can be accepted as exact domain evidence.** `src/creatidy_kernel/adapters/forgejo.py:86-90` validates only the `forgejo:` prefix. Branch and PR observations accept any nonempty revision string at `forgejo.py:128-138` and `forgejo.py:160-178`; PR creation can then return `ACCEPTED` at `forgejo.py:283-322` when those malformed values match the effect. The fake repeats the weak validation at `src/creatidy_kernel/adapters/fake_forge.py:163-180` and `fake_forge.py:237-249`, while `forgejo_transport.py:258-265` correctly requires a full 40- or 64-character hexadecimal object ID. Validate revision payloads using that full-OID grammar before producing observations or receipts, with shared malformed-revision coverage. 2. **Unsupported capabilities cannot be represented or rejected explicitly.** `ForgejoForge.capabilities()` always advertises all operations at `src/creatidy_kernel/adapters/forgejo.py:75-76`; the `UnsupportedForge` checks at `forgejo.py:244-256` test that same constant set and are unreachable. `FakeForge` is likewise fixed at `src/creatidy_kernel/adapters/fake_forge.py:123-124`. Configurations without PR or conditional-push support therefore advertise support and degrade into generic rejection/unknown outcomes. Derive capabilities from configured/probed transport support and cover supported/unsupported fixtures in the shared suite. 3. **Oversized numeric cursors violate fake/reference conformance.** `ForgejoForge._page()` catches integer conversion failure at `src/creatidy_kernel/adapters/forgejo.py:180-186`, while `FakeForge._page()` performs unguarded `int(cursor)` at `src/creatidy_kernel/adapters/fake_forge.py:182-192`. Python rejects decimal strings beyond its configured digit limit, so Forgejo fails closed but the fake raises. The same issue exists for known PR references at `fake_forge.py:326-337`. Bound input length or catch conversion failures consistently, and add long numeric cursor/reference cases. ### Non-blocking Suggestions None beyond the direct issue work above. ### Questions for the Author None. ### Security / Privacy Notes No merge operation, unconditional push, redirects, ambient credential helper, worker-controlled Git hooks, secrets, or private-runtime dependency is introduced. Git and HTTP operations have finite bounds and controlled environments. Blocking issue 1 remains a trust-boundary defect because malformed remote responses can be elevated into accepted domain evidence. ### Test / Documentation Gaps - Add shared malformed same-provider revision cases for branch observations, PR creation responses, authorized effects, and reconciliation. - Add explicit supported/unsupported capability fixtures. - Add oversized numeric cursor and change-reference cases. - No live Forgejo integration was run; this is residual validation rather than a substitute for the offline suite. - `README.md:9-18` still says the package does not contact providers or perform live effects, although this PR adds concrete HTTPS and credential-bearing Git transports. Distinguish not-autonomously-invoked from not-implemented. - `README.md:54-56` still describes the Forge port as future integration. ### Follow-Up Candidates None. These defects and documentation corrections are directly within issue #9. ### Overall Recommendation **REQUEST_CHANGES** for exact frozen HEAD `b35619fa2754b87fa44a9642665de30c99513741`. The authenticated Forgejo account (`kilo`) is also the PR author, so this substantive request-changes verdict is persisted as `COMMENT`; it is not a formal Forgejo `REQUEST_CHANGES` review.
fix: validate Forge revisions and negotiate capabilities
All checks were successful
ci / check (pull_request) Successful in 38s
7a33307fb7
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: 7a33307fb7efcc1174a6e75b91a0758ea2cd02c6
Linked issue: #9

Summary

The architecture direction, opaque qualified references, explicit capability negotiation, fail-closed observations, bounded transports, conservative uncertain replay, and absence of merge automation are aligned with issue #9. CI run #1967 succeeded for this exact head, and the PR reports make check (365 passed) plus make package-check passing. Live Forgejo integration was not run.

Three blocking correctness/authority defects remain on the frozen head.

Intent / Acceptance Evidence

Issue #9 criterion Assessment
Fake and Forgejo pass shared required-operation/receipt conformance Not met: fake accepts Git-invalid branch names that production rejects.
Identities remain opaque and qualified outside adapter Met.
Pagination, inaccessible/absent, stale, unsupported, and unknown outcomes remain distinct and safe Mostly met, but malformed producer PR numbers can create accepted references that cannot be observed or reconciled.
Only authorized exact-scope effects occur; transport success is not acknowledgement Not met: raw URL spelling does not establish that Git and HTTP target the same canonical repository.
PR opening tested; automatic merge absent; core tests require no Forgejo Met.

Architecture / Information Flow

Dependency direction remains adapters -> ports -> core; Forgejo payloads do not leak into core. No merge API, deployment behavior, GitHub canonical behavior, or universal SDK was added.

Blocking Issues

  1. Security/authority: Git remote binding compares raw URL spelling rather than a canonical remote target. src/creatidy_kernel/adapters/forgejo_transport.py:209-214 compares the raw URL path directly to the opaque reference payload. A reference such as forgejo:team/proj%65ct can bind Git to /team/proj%65ct.git, which a server interprets as team/project, while the HTTP adapter quotes % and addresses /repos/team/proj%2565ct. Git and HTTP can therefore act on different repositories for one supposedly exact repository reference. Parse and canonicalize the URL path, or reject percent escapes and other noncanonical repository components before accepting the transport.

  2. Idempotency: a PR receipt can be accepted with a reference that cannot later be observed or reconciled. src/creatidy_kernel/adapters/forgejo.py:199-205 accepts number when isinstance(number, int) and number >= 0; this admits 0, True, and arbitrarily long integers. Reference parsing later requires 1-18 nonzero ASCII digits (_number, used by change/reconcile). A malformed producer response can therefore yield an ACCEPTED receipt whose reference cannot be replayed. Apply the same invariant to producer payloads and reference parsing, using type(number) is int.

  3. Conformance: the fake accepts branch names that production rejects. Core validation at src/creatidy_kernel/core/forge.py:97-103 checks only emptiness/surrounding whitespace. FakeForge writes the branch directly at src/creatidy_kernel/adapters/fake_forge.py:294-299, while ConditionalGitTransport rejects invalid refs with git check-ref-format --branch at src/creatidy_kernel/adapters/forgejo_transport.py:305-307. For example, bad..name succeeds in fake-backed tests but fails in production, so the shared fake/reference conformance requirement is unmet. Validate the same branch grammar before either adapter performs an effect and add shared regression coverage.

Non-blocking Suggestions

None.

Questions for the Author

None.

Security / Privacy Notes

Blocking issue 1 is an exact-scope authority violation: the same logical repository handle can identify different Git and HTTP targets. No secret exposure was observed in this review.

Test / Documentation Gaps

Add shared adversarial cases for percent-escaped repository paths, producer PR numbers 0/True/more than 18 digits, and Git-invalid branch names. Documentation otherwise matches the implemented opt-in transport behavior.

Follow-Up Candidates

None. All missing work is required by issue #9 and belongs in this PR.

Overall Recommendation

REQUEST_CHANGES

Three blocking findings remain on exact HEAD 7a33307fb7efcc1174a6e75b91a0758ea2cd02c6. This review is persisted as COMMENT because the authenticated shared kilo account is also the PR author; COMMENT is not formal platform approval or a formal requested-changes state.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** `issue-9-forge-port` -> `develop` **Frozen head:** `7a33307fb7efcc1174a6e75b91a0758ea2cd02c6` **Linked issue:** #9 ### Summary The architecture direction, opaque qualified references, explicit capability negotiation, fail-closed observations, bounded transports, conservative uncertain replay, and absence of merge automation are aligned with issue #9. CI run #1967 succeeded for this exact head, and the PR reports `make check` (365 passed) plus `make package-check` passing. Live Forgejo integration was not run. Three blocking correctness/authority defects remain on the frozen head. ### Intent / Acceptance Evidence | Issue #9 criterion | Assessment | | --- | --- | | Fake and Forgejo pass shared required-operation/receipt conformance | **Not met:** fake accepts Git-invalid branch names that production rejects. | | Identities remain opaque and qualified outside adapter | Met. | | Pagination, inaccessible/absent, stale, unsupported, and unknown outcomes remain distinct and safe | Mostly met, but malformed producer PR numbers can create accepted references that cannot be observed or reconciled. | | Only authorized exact-scope effects occur; transport success is not acknowledgement | **Not met:** raw URL spelling does not establish that Git and HTTP target the same canonical repository. | | PR opening tested; automatic merge absent; core tests require no Forgejo | Met. | ### Architecture / Information Flow Dependency direction remains `adapters -> ports -> core`; Forgejo payloads do not leak into core. No merge API, deployment behavior, GitHub canonical behavior, or universal SDK was added. ### Blocking Issues 1. **Security/authority: Git remote binding compares raw URL spelling rather than a canonical remote target.** `src/creatidy_kernel/adapters/forgejo_transport.py:209-214` compares the raw URL path directly to the opaque reference payload. A reference such as `forgejo:team/proj%65ct` can bind Git to `/team/proj%65ct.git`, which a server interprets as `team/project`, while the HTTP adapter quotes `%` and addresses `/repos/team/proj%2565ct`. Git and HTTP can therefore act on different repositories for one supposedly exact repository reference. Parse and canonicalize the URL path, or reject percent escapes and other noncanonical repository components before accepting the transport. 2. **Idempotency: a PR receipt can be accepted with a reference that cannot later be observed or reconciled.** `src/creatidy_kernel/adapters/forgejo.py:199-205` accepts `number` when `isinstance(number, int)` and `number >= 0`; this admits `0`, `True`, and arbitrarily long integers. Reference parsing later requires 1-18 nonzero ASCII digits (`_number`, used by `change`/`reconcile`). A malformed producer response can therefore yield an `ACCEPTED` receipt whose reference cannot be replayed. Apply the same invariant to producer payloads and reference parsing, using `type(number) is int`. 3. **Conformance: the fake accepts branch names that production rejects.** Core validation at `src/creatidy_kernel/core/forge.py:97-103` checks only emptiness/surrounding whitespace. `FakeForge` writes the branch directly at `src/creatidy_kernel/adapters/fake_forge.py:294-299`, while `ConditionalGitTransport` rejects invalid refs with `git check-ref-format --branch` at `src/creatidy_kernel/adapters/forgejo_transport.py:305-307`. For example, `bad..name` succeeds in fake-backed tests but fails in production, so the shared fake/reference conformance requirement is unmet. Validate the same branch grammar before either adapter performs an effect and add shared regression coverage. ### Non-blocking Suggestions None. ### Questions for the Author None. ### Security / Privacy Notes Blocking issue 1 is an exact-scope authority violation: the same logical repository handle can identify different Git and HTTP targets. No secret exposure was observed in this review. ### Test / Documentation Gaps Add shared adversarial cases for percent-escaped repository paths, producer PR numbers `0`/`True`/more than 18 digits, and Git-invalid branch names. Documentation otherwise matches the implemented opt-in transport behavior. ### Follow-Up Candidates None. All missing work is required by issue #9 and belongs in this PR. ### Overall Recommendation **REQUEST_CHANGES** Three blocking findings remain on exact HEAD `7a33307fb7efcc1174a6e75b91a0758ea2cd02c6`. This review is persisted as `COMMENT` because the authenticated shared `kilo` account is also the PR author; `COMMENT` is not formal platform approval or a formal requested-changes state.
fix: align Forge repository and branch identities
All checks were successful
ci / check (pull_request) Successful in 28s
08f994f4ab
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: 08f994f4abf31601b03dc551633a9181cf6585ec
Linked issue: #9

Summary

The port direction, inward dependency layering, conservative PR replay, and lack of merge support align with issue #9. Exact-head CI run #1968 succeeded. The current head still has blocking exact-scope, fail-closed producer-validation, rejection-classification, and transport-boundary defects.

Intent / Acceptance Evidence

Issue #9 criterion Evidence Result
Shared fake/Forgejo conformance Parameterized offline suite covers observations/effects/replay Partial: malformed PR and check payloads still diverge or fabricate evidence
Opaque qualified identities Core uses Reference; adapters interpret Forgejo payloads Partial: repository path is not bound to one Forgejo origin
Distinct pagination/absence/stale/unsupported/unknown Broad synthetic coverage and bounded replay Partial: deterministic local capability/refusal cases become UNKNOWN
Only authorized exact-scope effects Authorization predicate plus expected-old Git push Fails: HTTP and Git transports may target different origins under one reference
PR opening tested; no merge PR artifact tests exist; no merge operation is exposed Pass, subject to receipt/rejection findings below

Architecture / Information Flow

Dependency direction remains adapters -> ports -> core. Core does not import Forgejo payloads, live Forgejo, or private infrastructure. The blocking issues occur at untrusted producer and credential-bearing adapter boundaries, where ADR 0003/0005 and docs/architecture/contracts.md require exact subjects, domain rejection distinct from uncertainty, and bounded fail-closed behavior.

Blocking Issues

  1. Exact repository authority is not bound across transports (src/creatidy_kernel/adapters/forgejo_transport.py:199-212, src/creatidy_kernel/adapters/forgejo.py:76-87). ConditionalGitTransport validates only the raw owner/repo suffix. A ForgejoForge can observe forgejo:team/project through https://canonical.example/api/v1 while pushing the same authorized effect to https://other.example/team/project.git. Neither the opaque reference nor authorize(effect) distinguishes those installations. Composition must reject mismatched origins or the reference/configuration must carry an equivalent exact forge-instance binding.

  2. Malformed synthetic PRs can escape fail-closed handling (src/creatidy_kernel/adapters/fake_forge.py:165-177). change() indexes pull['number'] before _pull() validates the record. A producer-shaped record missing number raises KeyError instead of returning Presence.UNKNOWN, violating fake/reference conformance. The malformed-record test covers invalid values but not a missing field.

  3. Boolean check IDs fabricate exact passed-check evidence (src/creatidy_kernel/adapters/fake_forge.py:235-256, src/creatidy_kernel/adapters/forgejo.py:221-243). Both adapters use isinstance(check_id, int), so JSON true becomes the exact reference forgejo:team/project@True and may carry PASSED. Require type(check_id) is int and the producer's finite integer range; add shared malformed-ID cases.

  4. Known Forgejo PR refusals are classified as uncertain (src/creatidy_kernel/adapters/forgejo.py:318-334). Only 403/422 become REJECTED; documented non-creation responses such as 409 conflict, 413 quota exceeded, and 423 archived repository become UNKNOWN. These responses establish a domain refusal rather than an uncertain transport outcome. Map documented deterministic refusals explicitly and test replay behavior.

  5. Advertised HTTP capabilities can be deterministically unusable (src/creatidy_kernel/adapters/forgejo_transport.py:51-59,76-90; src/creatidy_kernel/adapters/forgejo.py:88-101). supports_reads and supports_pr are always true, but _deadline() refuses every operation outside the main thread or without setitimer, before token access or network I/O. PR apply then reports UNKNOWN, falsely implying a remote effect may have occurred, instead of explicit unsupported/local refusal. Capability negotiation must reflect the execution context or dispatch must reject before an uncertain receipt is possible.

  6. A successful Git parent can leave credential-bearing descendants alive (src/creatidy_kernel/adapters/forgejo_transport.py:146-190). Process-group cleanup occurs for exceptions and nonzero exit only. A zero-exit Git/askpass process can spawn a child with closed output descriptors; selector draining and parent success return while the descendant survives past the configured deadline. Ensure the process group is settled/terminated on every completion path and add the corresponding successful-parent fixture.

  7. Bounded hostile JSON can raise uncaught parser exceptions (src/creatidy_kernel/adapters/forgejo_transport.py:127-143). json.loads() catches only decoding errors. Deep nesting can raise RecursionError, and pathological numeric literals can raise ValueError; both escape rather than becoming malformed/unknown producer data. Normalize all bounded parse failures without hiding transport outcomes.

  8. Reference/path validation is internally inconsistent and unbounded (src/creatidy_kernel/adapters/forge_refs.py:10-35, src/creatidy_kernel/adapters/forgejo_transport.py:96-109). repository_path() permits team/pro..ject, while the HTTP transport rejects every path containing ..; the Git side accepts the same repository, creating another split boundary. Repository and branch values also have no finite length limit, allowing unbounded URL quoting/path allocation before credential access. Define one canonical finite grammar and exercise it across fake, HTTP, and Git transports.

  9. Git object IDs are accepted case-insensitively but compared case-sensitively (src/creatidy_kernel/adapters/forgejo.py:65-69,108-115,155-164,287-317). An authorized uppercase full OID and the equivalent lowercase Forgejo observation compare as different Reference values, producing false STALE/UNKNOWN results. Canonicalize adapter-owned OIDs (or consistently reject noncanonical spelling) and test uppercase input against lowercase producer payloads.

Non-blocking Suggestions

None beyond the blocking corrections above.

Questions for the Author

None. The findings are bounded implementation corrections within issue #9 and the accepted architecture.

Security / Privacy Notes

The cross-origin mutation path and surviving successful-process descendants affect exact authority and scoped-credential containment. No secret material was inspected, and no live Forgejo integration or PR code was executed during this review.

Test / Documentation Gaps

Add producer-shaped cases for missing PR fields, boolean/out-of-range check IDs, lowercase producer OIDs against uppercase inputs, all documented PR refusal statuses, mismatched HTTP/Git origins, non-main-thread capability behavior, successful-parent descendants, hostile bounded JSON, oversized references, and consecutive-dot repository names.

Follow-Up Candidates

None. These are required issue #9 acceptance corrections, not distinct follow-up work.

Overall Recommendation

REQUEST_CHANGES for frozen HEAD 08f994f4abf31601b03dc551633a9181cf6585ec.

Because the authenticated reviewer is the shared kilo PR-author account, this substantive verdict is persisted as a Forgejo COMMENT, not a formal platform REQUEST_CHANGES or approval.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** issue-9-forge-port -> develop **Frozen head:** `08f994f4abf31601b03dc551633a9181cf6585ec` **Linked issue:** #9 ### Summary The port direction, inward dependency layering, conservative PR replay, and lack of merge support align with issue #9. Exact-head CI run #1968 succeeded. The current head still has blocking exact-scope, fail-closed producer-validation, rejection-classification, and transport-boundary defects. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence | Result | | --- | --- | --- | | Shared fake/Forgejo conformance | Parameterized offline suite covers observations/effects/replay | Partial: malformed PR and check payloads still diverge or fabricate evidence | | Opaque qualified identities | Core uses `Reference`; adapters interpret Forgejo payloads | Partial: repository path is not bound to one Forgejo origin | | Distinct pagination/absence/stale/unsupported/unknown | Broad synthetic coverage and bounded replay | Partial: deterministic local capability/refusal cases become `UNKNOWN` | | Only authorized exact-scope effects | Authorization predicate plus expected-old Git push | Fails: HTTP and Git transports may target different origins under one reference | | PR opening tested; no merge | PR artifact tests exist; no merge operation is exposed | Pass, subject to receipt/rejection findings below | ### Architecture / Information Flow Dependency direction remains `adapters -> ports -> core`. Core does not import Forgejo payloads, live Forgejo, or private infrastructure. The blocking issues occur at untrusted producer and credential-bearing adapter boundaries, where ADR 0003/0005 and `docs/architecture/contracts.md` require exact subjects, domain rejection distinct from uncertainty, and bounded fail-closed behavior. ### Blocking Issues 1. **Exact repository authority is not bound across transports** (`src/creatidy_kernel/adapters/forgejo_transport.py:199-212`, `src/creatidy_kernel/adapters/forgejo.py:76-87`). `ConditionalGitTransport` validates only the raw `owner/repo` suffix. A `ForgejoForge` can observe `forgejo:team/project` through `https://canonical.example/api/v1` while pushing the same authorized effect to `https://other.example/team/project.git`. Neither the opaque reference nor `authorize(effect)` distinguishes those installations. Composition must reject mismatched origins or the reference/configuration must carry an equivalent exact forge-instance binding. 2. **Malformed synthetic PRs can escape fail-closed handling** (`src/creatidy_kernel/adapters/fake_forge.py:165-177`). `change()` indexes `pull['number']` before `_pull()` validates the record. A producer-shaped record missing `number` raises `KeyError` instead of returning `Presence.UNKNOWN`, violating fake/reference conformance. The malformed-record test covers invalid values but not a missing field. 3. **Boolean check IDs fabricate exact passed-check evidence** (`src/creatidy_kernel/adapters/fake_forge.py:235-256`, `src/creatidy_kernel/adapters/forgejo.py:221-243`). Both adapters use `isinstance(check_id, int)`, so JSON `true` becomes the exact reference `forgejo:team/project@True` and may carry `PASSED`. Require `type(check_id) is int` and the producer's finite integer range; add shared malformed-ID cases. 4. **Known Forgejo PR refusals are classified as uncertain** (`src/creatidy_kernel/adapters/forgejo.py:318-334`). Only 403/422 become `REJECTED`; documented non-creation responses such as 409 conflict, 413 quota exceeded, and 423 archived repository become `UNKNOWN`. These responses establish a domain refusal rather than an uncertain transport outcome. Map documented deterministic refusals explicitly and test replay behavior. 5. **Advertised HTTP capabilities can be deterministically unusable** (`src/creatidy_kernel/adapters/forgejo_transport.py:51-59,76-90`; `src/creatidy_kernel/adapters/forgejo.py:88-101`). `supports_reads` and `supports_pr` are always true, but `_deadline()` refuses every operation outside the main thread or without `setitimer`, before token access or network I/O. PR apply then reports `UNKNOWN`, falsely implying a remote effect may have occurred, instead of explicit unsupported/local refusal. Capability negotiation must reflect the execution context or dispatch must reject before an uncertain receipt is possible. 6. **A successful Git parent can leave credential-bearing descendants alive** (`src/creatidy_kernel/adapters/forgejo_transport.py:146-190`). Process-group cleanup occurs for exceptions and nonzero exit only. A zero-exit Git/askpass process can spawn a child with closed output descriptors; selector draining and parent success return while the descendant survives past the configured deadline. Ensure the process group is settled/terminated on every completion path and add the corresponding successful-parent fixture. 7. **Bounded hostile JSON can raise uncaught parser exceptions** (`src/creatidy_kernel/adapters/forgejo_transport.py:127-143`). `json.loads()` catches only decoding errors. Deep nesting can raise `RecursionError`, and pathological numeric literals can raise `ValueError`; both escape rather than becoming malformed/unknown producer data. Normalize all bounded parse failures without hiding transport outcomes. 8. **Reference/path validation is internally inconsistent and unbounded** (`src/creatidy_kernel/adapters/forge_refs.py:10-35`, `src/creatidy_kernel/adapters/forgejo_transport.py:96-109`). `repository_path()` permits `team/pro..ject`, while the HTTP transport rejects every path containing `..`; the Git side accepts the same repository, creating another split boundary. Repository and branch values also have no finite length limit, allowing unbounded URL quoting/path allocation before credential access. Define one canonical finite grammar and exercise it across fake, HTTP, and Git transports. 9. **Git object IDs are accepted case-insensitively but compared case-sensitively** (`src/creatidy_kernel/adapters/forgejo.py:65-69,108-115,155-164,287-317`). An authorized uppercase full OID and the equivalent lowercase Forgejo observation compare as different `Reference` values, producing false `STALE`/`UNKNOWN` results. Canonicalize adapter-owned OIDs (or consistently reject noncanonical spelling) and test uppercase input against lowercase producer payloads. ### Non-blocking Suggestions None beyond the blocking corrections above. ### Questions for the Author None. The findings are bounded implementation corrections within issue #9 and the accepted architecture. ### Security / Privacy Notes The cross-origin mutation path and surviving successful-process descendants affect exact authority and scoped-credential containment. No secret material was inspected, and no live Forgejo integration or PR code was executed during this review. ### Test / Documentation Gaps Add producer-shaped cases for missing PR fields, boolean/out-of-range check IDs, lowercase producer OIDs against uppercase inputs, all documented PR refusal statuses, mismatched HTTP/Git origins, non-main-thread capability behavior, successful-parent descendants, hostile bounded JSON, oversized references, and consecutive-dot repository names. ### Follow-Up Candidates None. These are required issue #9 acceptance corrections, not distinct follow-up work. ### Overall Recommendation **REQUEST_CHANGES** for frozen HEAD `08f994f4abf31601b03dc551633a9181cf6585ec`. Because the authenticated reviewer is the shared `kilo` PR-author account, this substantive verdict is persisted as a Forgejo `COMMENT`, not a formal platform `REQUEST_CHANGES` or approval.
kilo left a comment

Architecture / Model-Correction Review

Frozen HEAD: 08f994f4abf31601b03dc551633a9181cf6585ec
Base: develop@8877b97b83629e942e1b8725dfd02bb70c658b27
Scope: issue #9, the complete PR, accepted ADR 0003/0004/0005, docs/architecture/contracts.md, the threat model, and full review F33-F41. This is not full PR approval and is not a merge recommendation.

Verdict

BOUNDED_CORRECTION

One coherent correction remains feasible within the final three authorized cycles. No new owner authority, architecture, security policy, service, merge capability, or scope expansion is required. The accepted architecture already requires exact repository authority, finite external operations, validated producer evidence, explicit unsupported capabilities, and shared fake/reference conformance. Repeated remediation is not itself a Human Gate under ADR 0005.

Finding Disposition

  • F33 is valid and architectural only in consequence, not in decision. A repository path plus provider name does not bind a Forgejo installation. Keep the core handle opaque; give each trusted transport a canonical non-secret instance identity derived from normalized HTTPS origin, and reject adapter construction unless HTTP and Git identities match. Bind repository path separately using one canonical grammar. No URL or Forgejo payload needs to enter core.
  • F34, F35, F39, and F41 are valid producer-validation defects. Use one adapter-owned validation layer for required PR fields, positive bounded integer IDs (type(value) is int), bounded JSON parse failures, and canonical Git OIDs. The smallest safe OID rule is lowercase full 40/64 hex at the boundary; uppercase/noncanonical input may be rejected rather than supported.
  • F36 is valid with a constraint. Map only documented, endpoint-specific responses that prove PR non-creation to REJECTED; authentication remains INACCESSIBLE, malformed/unrecognized responses remain UNKNOWN, and transient or ambiguous statuses must not be guessed into rejection. Do not create a universal HTTP-status policy.
  • F37 is valid. The concrete HTTP transport cannot advertise operations in an execution context where its total deadline is unavailable. Make support dynamic/preflighted and raise explicit UnsupportedForge before token access or network I/O. Non-main-thread and no-setitimer live use may safely remain unsupported; do not weaken the deadline or report remote uncertainty for a deterministic local refusal.
  • F38 is valid. Settle the Git process group on every return path, including a zero-exit parent, before interpreting bounded porcelain. Failure to prove group quiescence is UNKNOWN; it is not acceptance.
  • F40 is valid as a shared finite-grammar defect, but banning all consecutive dots is not inherently required. Replace raw substring path checks with one canonical owner/repository/branch parser used by fake, HTTP, and Git boundaries, with finite encoded-byte limits and exact segment traversal rejection. Either consistently support a safe canonical pro..ject or consistently reject it; split behavior is the defect.

One Coherent Minimal Correction

  1. Create one immutable adapter binding: canonical Forgejo instance identity plus canonical repository path. HTTP and Git transports expose that binding; ForgejoForge rejects mismatched composition before capabilities or credentials are available. The synthetic transport exposes the same binding for shared tests.
  2. Create one finite boundary codec: validate and normalize repository/branch/change/check/OID values and producer PR/check payloads once. Reuse it in fake and Forgejo paths. Reject noncanonical or oversized local inputs before allocation/I/O; malformed remote data becomes UNKNOWN/incomplete evidence, never an exception or accepted receipt.
  3. Make transport support and lifetime truthful: capability preflight includes deadline availability; request-size refusal and unsupported context occur before I/O; bounded JSON failures become malformed producer data; every Git process group is terminated/reaped or the outcome stays UNKNOWN.
  4. Centralize receipt classification: accept only exact validated producer receipts; map only enumerated endpoint-specific deterministic PR refusals to REJECTED; preserve all other ambiguity as UNKNOWN. Add one shared adversarial matrix covering mismatched origins, missing PR fields, bool/out-of-range IDs, hostile JSON, canonical OIDs, finite reference limits, context-disabled HTTP, successful-parent descendants, and refusal statuses.

Safe Unsupported / Fail-Closed Boundary

It is safe for this issue to leave live HTTP unsupported where a true total deadline cannot be enforced, reject mismatched or noncanonical instance/repository configuration at construction, reject uppercase or oversized identifiers, and return UNKNOWN for unrecognized producer/status/process evidence. It is not safe to advertise unusable capabilities, infer cross-instance equality from owner/repo, accept a parent exit as process-group completion, or broaden REJECTED to ambiguous transport responses.

The existing public Forge concepts and adapters -> ports -> core direction are sufficient. Preserve same-repository PR scope, the trusted controller-owned object source, conservative known-reference replay, and no merge operation. This correction does not require a new service, universal Forge SDK, custom sandbox, secrets manager, private dependency, or relaxed issue criterion.

I reverified the PR immediately before persistence: HEAD and base remain the frozen commits above. I did not execute PR code, edit the branch, launch another reviewer, or merge. Persisted state is COMMENT only.

## Architecture / Model-Correction Review **Frozen HEAD:** `08f994f4abf31601b03dc551633a9181cf6585ec` **Base:** `develop@8877b97b83629e942e1b8725dfd02bb70c658b27` **Scope:** issue #9, the complete PR, accepted ADR 0003/0004/0005, `docs/architecture/contracts.md`, the threat model, and full review F33-F41. This is not full PR approval and is not a merge recommendation. ### Verdict **BOUNDED_CORRECTION** One coherent correction remains feasible within the final three authorized cycles. No new owner authority, architecture, security policy, service, merge capability, or scope expansion is required. The accepted architecture already requires exact repository authority, finite external operations, validated producer evidence, explicit unsupported capabilities, and shared fake/reference conformance. Repeated remediation is not itself a Human Gate under ADR 0005. ### Finding Disposition - **F33 is valid and architectural only in consequence, not in decision.** A repository path plus provider name does not bind a Forgejo installation. Keep the core handle opaque; give each trusted transport a canonical non-secret instance identity derived from normalized HTTPS origin, and reject adapter construction unless HTTP and Git identities match. Bind repository path separately using one canonical grammar. No URL or Forgejo payload needs to enter core. - **F34, F35, F39, and F41 are valid producer-validation defects.** Use one adapter-owned validation layer for required PR fields, positive bounded integer IDs (`type(value) is int`), bounded JSON parse failures, and canonical Git OIDs. The smallest safe OID rule is lowercase full 40/64 hex at the boundary; uppercase/noncanonical input may be rejected rather than supported. - **F36 is valid with a constraint.** Map only documented, endpoint-specific responses that prove PR non-creation to `REJECTED`; authentication remains `INACCESSIBLE`, malformed/unrecognized responses remain `UNKNOWN`, and transient or ambiguous statuses must not be guessed into rejection. Do not create a universal HTTP-status policy. - **F37 is valid.** The concrete HTTP transport cannot advertise operations in an execution context where its total deadline is unavailable. Make support dynamic/preflighted and raise explicit `UnsupportedForge` before token access or network I/O. Non-main-thread and no-`setitimer` live use may safely remain unsupported; do not weaken the deadline or report remote uncertainty for a deterministic local refusal. - **F38 is valid.** Settle the Git process group on every return path, including a zero-exit parent, before interpreting bounded porcelain. Failure to prove group quiescence is `UNKNOWN`; it is not acceptance. - **F40 is valid as a shared finite-grammar defect, but banning all consecutive dots is not inherently required.** Replace raw substring path checks with one canonical owner/repository/branch parser used by fake, HTTP, and Git boundaries, with finite encoded-byte limits and exact segment traversal rejection. Either consistently support a safe canonical `pro..ject` or consistently reject it; split behavior is the defect. ### One Coherent Minimal Correction 1. **Create one immutable adapter binding:** canonical Forgejo instance identity plus canonical repository path. HTTP and Git transports expose that binding; `ForgejoForge` rejects mismatched composition before capabilities or credentials are available. The synthetic transport exposes the same binding for shared tests. 2. **Create one finite boundary codec:** validate and normalize repository/branch/change/check/OID values and producer PR/check payloads once. Reuse it in fake and Forgejo paths. Reject noncanonical or oversized local inputs before allocation/I/O; malformed remote data becomes `UNKNOWN`/incomplete evidence, never an exception or accepted receipt. 3. **Make transport support and lifetime truthful:** capability preflight includes deadline availability; request-size refusal and unsupported context occur before I/O; bounded JSON failures become malformed producer data; every Git process group is terminated/reaped or the outcome stays `UNKNOWN`. 4. **Centralize receipt classification:** accept only exact validated producer receipts; map only enumerated endpoint-specific deterministic PR refusals to `REJECTED`; preserve all other ambiguity as `UNKNOWN`. Add one shared adversarial matrix covering mismatched origins, missing PR fields, bool/out-of-range IDs, hostile JSON, canonical OIDs, finite reference limits, context-disabled HTTP, successful-parent descendants, and refusal statuses. ### Safe Unsupported / Fail-Closed Boundary It is safe for this issue to leave live HTTP unsupported where a true total deadline cannot be enforced, reject mismatched or noncanonical instance/repository configuration at construction, reject uppercase or oversized identifiers, and return `UNKNOWN` for unrecognized producer/status/process evidence. It is not safe to advertise unusable capabilities, infer cross-instance equality from `owner/repo`, accept a parent exit as process-group completion, or broaden `REJECTED` to ambiguous transport responses. The existing public `Forge` concepts and `adapters -> ports -> core` direction are sufficient. Preserve same-repository PR scope, the trusted controller-owned object source, conservative known-reference replay, and no merge operation. This correction does not require a new service, universal Forge SDK, custom sandbox, secrets manager, private dependency, or relaxed issue criterion. I reverified the PR immediately before persistence: HEAD and base remain the frozen commits above. I did not execute PR code, edit the branch, launch another reviewer, or merge. Persisted state is `COMMENT` only.
fix: bind Forge instance and normalize transport evidence
All checks were successful
ci / check (pull_request) Successful in 29s
a62e8a7159
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: a62e8a7159c50af0c6adfffa92bc4101abb7f4bd
Linked issue: #9

Summary

The dependency direction, opaque core references, conditional Git update, canonical Forge binding, fail-closed observation model, and absence of merge automation are sound. Three blocking defects remain at this exact head.

Intent / Acceptance Evidence

Issue #9 criterion Evidence Result
Fake and Forgejo share required-operation/receipt conformance Broad parameterized coverage exists, but unsupported reads and out-of-binding repositories produce different behavior Not met
Identities remain opaque and qualified outside adapters Reference stays provider-qualified; Forgejo interpretation remains in adapters Met
Absence/inaccessible/unknown/stale/unsupported remain distinct and safe Explicit enums and conservative remote handling are present Mostly met
Only authorized exact-scope effects occur PR creation uses mutable branch names after a preflight observation Not met
PR opening tested; no merge; core needs no Forgejo Tests cover PR creation; no merge operation exists; core remains provider-neutral Met, subject to exact-scope defect

Architecture / Information Flow

adapters -> ports -> core is preserved. HTTPS origin/repository binding, redirect refusal, controlled Git configuration, exact conditional push, bounded subprocess capture, process-group cleanup, scoped credentials, and conservative 404 handling are positive controls.

Blocking Issues

  1. PR creation can perform an effect outside the authorized exact revision scope (src/creatidy_kernel/adapters/forgejo.py:322-360). The adapter observes head@A and base@B, then POSTs only mutable branch names. If either branch moves before Forgejo handles the POST, Forgejo can create a PR for different revisions. Post-creation payload checks can return UNKNOWN, but the unauthorized artifact already exists. This violates the exact-scope acceptance criterion. The pr capability needs a race-free creation mechanism or must not be advertised when exact revision binding cannot be enforced.

  2. Fake/reference conformance diverges for unavailable reads and repository binding (src/creatidy_kernel/adapters/fake_forge.py:132-152, src/creatidy_kernel/adapters/forgejo.py:103-107,128-129,265-267). FakeForge.capabilities() removes read capabilities when supports_reads is false, yet its observation methods still return state; ForgejoForge raises UnsupportedForge. A valid repository outside the configured binding returns UNKNOWN in the fake but raises ForgeConflict in the reference adapter. Shared tests do not cover these cases, allowing fake-tested callers to depend on behavior unavailable in production.

  3. PR size limits occur after unbounded marker/body construction (src/creatidy_kernel/core/forge.py:74-118, src/creatidy_kernel/adapters/forgejo.py:328-334, src/creatidy_kernel/adapters/forgejo_transport.py:124-134). Effect/OperationKey text fields have no finite bound. effect_marker() serializes and Base64-encodes the complete operation identity, and apply() concatenates the PR body before the transport enforces max_bytes. Large authorized inputs can consume controller memory/CPU before deterministic local refusal. Validate finite inputs before encoding/concatenation and return the existing local rejection without network I/O.

Non-blocking Suggestions

None.

Questions for the Author

None.

Security / Privacy Notes

No credential disclosure or private-state dependency was found. The first and third blockers affect authority and trusted-controller resource boundaries.

Test / Documentation Gaps

Add shared conformance cases for unsupported reads and valid cross-binding repositories. Add adversarial tests proving oversized operation/title/body inputs are rejected before marker/body allocation. A live Forgejo race integration was not run; post-creation validation is insufficient by inspection.

Follow-Up Candidates

None. These findings are required by issue #9 and belong in this PR.

Overall Recommendation

REQUEST_CHANGES

Three blocking findings remain for frozen HEAD a62e8a7159c50af0c6adfffa92bc4101abb7f4bd. This review is persisted as COMMENT because the authenticated shared kilo identity is also the PR author; this is not a formal Forgejo requested-changes state.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** `issue-9-forge-port` -> `develop` **Frozen head:** `a62e8a7159c50af0c6adfffa92bc4101abb7f4bd` **Linked issue:** #9 ### Summary The dependency direction, opaque core references, conditional Git update, canonical Forge binding, fail-closed observation model, and absence of merge automation are sound. Three blocking defects remain at this exact head. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence | Result | |---|---|---| | Fake and Forgejo share required-operation/receipt conformance | Broad parameterized coverage exists, but unsupported reads and out-of-binding repositories produce different behavior | Not met | | Identities remain opaque and qualified outside adapters | `Reference` stays provider-qualified; Forgejo interpretation remains in adapters | Met | | Absence/inaccessible/unknown/stale/unsupported remain distinct and safe | Explicit enums and conservative remote handling are present | Mostly met | | Only authorized exact-scope effects occur | PR creation uses mutable branch names after a preflight observation | Not met | | PR opening tested; no merge; core needs no Forgejo | Tests cover PR creation; no merge operation exists; core remains provider-neutral | Met, subject to exact-scope defect | ### Architecture / Information Flow `adapters -> ports -> core` is preserved. HTTPS origin/repository binding, redirect refusal, controlled Git configuration, exact conditional push, bounded subprocess capture, process-group cleanup, scoped credentials, and conservative 404 handling are positive controls. ### Blocking Issues 1. **PR creation can perform an effect outside the authorized exact revision scope** (`src/creatidy_kernel/adapters/forgejo.py:322-360`). The adapter observes `head@A` and `base@B`, then POSTs only mutable branch names. If either branch moves before Forgejo handles the POST, Forgejo can create a PR for different revisions. Post-creation payload checks can return `UNKNOWN`, but the unauthorized artifact already exists. This violates the exact-scope acceptance criterion. The `pr` capability needs a race-free creation mechanism or must not be advertised when exact revision binding cannot be enforced. 2. **Fake/reference conformance diverges for unavailable reads and repository binding** (`src/creatidy_kernel/adapters/fake_forge.py:132-152`, `src/creatidy_kernel/adapters/forgejo.py:103-107,128-129,265-267`). `FakeForge.capabilities()` removes read capabilities when `supports_reads` is false, yet its observation methods still return state; `ForgejoForge` raises `UnsupportedForge`. A valid repository outside the configured binding returns `UNKNOWN` in the fake but raises `ForgeConflict` in the reference adapter. Shared tests do not cover these cases, allowing fake-tested callers to depend on behavior unavailable in production. 3. **PR size limits occur after unbounded marker/body construction** (`src/creatidy_kernel/core/forge.py:74-118`, `src/creatidy_kernel/adapters/forgejo.py:328-334`, `src/creatidy_kernel/adapters/forgejo_transport.py:124-134`). `Effect`/`OperationKey` text fields have no finite bound. `effect_marker()` serializes and Base64-encodes the complete operation identity, and `apply()` concatenates the PR body before the transport enforces `max_bytes`. Large authorized inputs can consume controller memory/CPU before deterministic local refusal. Validate finite inputs before encoding/concatenation and return the existing local rejection without network I/O. ### Non-blocking Suggestions None. ### Questions for the Author None. ### Security / Privacy Notes No credential disclosure or private-state dependency was found. The first and third blockers affect authority and trusted-controller resource boundaries. ### Test / Documentation Gaps Add shared conformance cases for unsupported reads and valid cross-binding repositories. Add adversarial tests proving oversized operation/title/body inputs are rejected before marker/body allocation. A live Forgejo race integration was not run; post-creation validation is insufficient by inspection. ### Follow-Up Candidates None. These findings are required by issue #9 and belong in this PR. ### Overall Recommendation REQUEST_CHANGES Three blocking findings remain for frozen HEAD `a62e8a7159c50af0c6adfffa92bc4101abb7f4bd`. This review is persisted as `COMMENT` because the authenticated shared `kilo` identity is also the PR author; this is not a formal Forgejo requested-changes state.
kilo left a comment

Architecture / Model-Correction Review

Frozen head: a62e8a7159c50af0c6adfffa92bc4101abb7f4bd
Frozen base: develop@8877b97b83629e942e1b8725dfd02bb70c658b27
Selected issue: #9
Verdict: GENUINE_OWNER_DECISION_REQUIRED

F42 is not a bounded implementation correction under the current contract. Issue #9 simultaneously requires authorized Forgejo PR effects, only authorized exact-scope effects, shared fake/reference required-operation conformance, and tested PR opening. At this head, ForgejoForge.apply() observes exact head/base revisions, then calls POST /pulls with only mutable branch names (src/creatidy_kernel/adapters/forgejo.py:322-335). Either ref may move before Forgejo resolves the POST. Response and branch checks at lines 344-360 can classify the outcome UNKNOWN, but cannot prevent or undo the already-created out-of-scope artifact.

ADR 0003 permits an effect only with external idempotency/CAS or an enforced exclusive capability and says unsafe automatic operations remain unsupported otherwise. ADR 0005 binds dangerous authority to the exact effect/input and rejects observe-then-unconditional mutation races. ADR 0004 and contracts.md require capability-gated conditional mutations rather than weakening correctness for an adapter.

No legitimate bounded race-free mechanism is present for this Forgejo endpoint. A pre/post check is observational, cleanup is too late, and a temporary/protected branch is still a mutable name unless a new, proven exclusive authority/locking mechanism prevents every competing writer through creation. Adding such exclusivity or selecting another API/effect model changes authority/architecture and requires owner authorization.

Disabling the live pr capability while retaining synthetic PR opening and fail-closed reference behavior would safely avoid the unauthorized effect, but it would materially change selected scope: the Forgejo reference adapter would no longer implement the issue's in-scope authorized PR operation or satisfy the same required-operation contract as the fake. That is an owner decision to revise issue #9, not an inferred remediation.

F43 (fake/reference conformance) and F44 (pre-encoding bounds) remain bounded defects if this scope continues, but neither resolves or overrides F42. This COMMENT is a correction-only architecture verdict, not full PR approval.

## Architecture / Model-Correction Review **Frozen head:** `a62e8a7159c50af0c6adfffa92bc4101abb7f4bd` **Frozen base:** `develop@8877b97b83629e942e1b8725dfd02bb70c658b27` **Selected issue:** #9 **Verdict:** `GENUINE_OWNER_DECISION_REQUIRED` F42 is not a bounded implementation correction under the current contract. Issue #9 simultaneously requires authorized Forgejo PR effects, only authorized exact-scope effects, shared fake/reference required-operation conformance, and tested PR opening. At this head, `ForgejoForge.apply()` observes exact head/base revisions, then calls `POST /pulls` with only mutable branch names (`src/creatidy_kernel/adapters/forgejo.py:322-335`). Either ref may move before Forgejo resolves the POST. Response and branch checks at lines 344-360 can classify the outcome `UNKNOWN`, but cannot prevent or undo the already-created out-of-scope artifact. ADR 0003 permits an effect only with external idempotency/CAS or an enforced exclusive capability and says unsafe automatic operations remain unsupported otherwise. ADR 0005 binds dangerous authority to the exact effect/input and rejects observe-then-unconditional mutation races. ADR 0004 and `contracts.md` require capability-gated conditional mutations rather than weakening correctness for an adapter. No legitimate bounded race-free mechanism is present for this Forgejo endpoint. A pre/post check is observational, cleanup is too late, and a temporary/protected branch is still a mutable name unless a new, proven exclusive authority/locking mechanism prevents every competing writer through creation. Adding such exclusivity or selecting another API/effect model changes authority/architecture and requires owner authorization. Disabling the live `pr` capability while retaining synthetic PR opening and fail-closed reference behavior would safely avoid the unauthorized effect, but it would materially change selected scope: the Forgejo reference adapter would no longer implement the issue's in-scope authorized PR operation or satisfy the same required-operation contract as the fake. That is an owner decision to revise issue #9, not an inferred remediation. F43 (fake/reference conformance) and F44 (pre-encoding bounds) remain bounded defects if this scope continues, but neither resolves or overrides F42. This COMMENT is a correction-only architecture verdict, not full PR approval.
fix: bind PR creation to isolated accepted head
All checks were successful
ci / check (pull_request) Successful in 30s
cda6861c15
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: cda6861c15b567de683b81cb6989826f3cdd9bd7
Linked issue: #9 [K5] Forge port and Forgejo adapter

Summary

The port and adapter preserve the intended inward dependency direction, opaque qualified references, conditional Git effects, exact PR-head verification, base repository/branch identity, and absence of merge/deploy capability. Exact-head Forgejo workflow run 1973 succeeded. Three blocking issues remain in snapshot authority/recovery and fake/reference conformance.

Intent / Acceptance Evidence

Issue #9 criterion Evidence at frozen head Result
Shared fake/Forgejo required-operation and receipt conformance Parameterized coverage is extensive, but immediate malformed PR evidence raises from the fake while Forgejo returns UNKNOWN Blocked
Opaque qualified identities and adapter-local Forgejo semantics core/forge.py, forge_refs.py, binding and codec tests Satisfied
Distinct pagination, inaccessible/absent/unknown, stale head, unsupported capability, unknown effects General observation coverage exists, but snapshot verification converts unavailable reads into ForgeConflict Blocked
Exact accepted SHA through a write-once snapshot created absent-to-exact and isolated through POST/verification Snapshot lock and exact-SHA checks exist, but a fresh delivery adopts a pre-existing same-SHA snapshot without executing absent-target CAS Blocked
Base authority is repository plus target branch identity; observed SHA is provenance PR response/reconciliation validate base ref identity and receipts retain observed base SHA; base movement test exists Satisfied
No merge; stale/moving head, contention, base movement, bounded input cases No merge capability and broad synthetic coverage Partially satisfied pending blockers

Architecture / Information Flow

adapters -> ports -> core is preserved. Forgejo payload parsing remains at the adapter boundary, and the core owns normalized references, observations, effects, and receipts. The optional live transports are injected and are not invoked autonomously.

Blocking Issues

  1. A fresh PR delivery adopts a pre-existing same-SHA snapshot instead of proving absent-to-exact creation. In src/creatidy_kernel/adapters/forgejo.py:362-368 and src/creatidy_kernel/adapters/fake_forge.py:389-393, an existing snapshot is accepted whenever its current SHA equals the authorized candidate, so compare_and_push(..., expected=None, ...) is skipped. The isolation guard prevents mutation while held, but it does not prove that this delivery created the ref or that the existing ref is Kernel-controlled. This contradicts issue #9 and the PR description's explicit absent-to-exact conditional-creation requirement. tests/test_forge.py:426-444 currently pre-seeds the ref and expects a first delivery to adopt it, encoding the unsafe behavior. A first delivery must require absent-target CAS and treat any pre-existing ref as contention; uncertain later delivery must remain reconciliation-only and must not POST without a trusted known PR reference.

  2. Unavailable snapshot verification reads are misclassified as immutable authority conflicts. src/creatidy_kernel/adapters/forgejo.py:375-376, 402-403, and 442-443 compare only .revision. UNKNOWN or INACCESSIBLE observations have revision=None, so transient, permission-limited, or eventually consistent reads raise ForgeConflict just like a positively observed mismatched SHA. Issue #9 explicitly requires unavailable reads, unknown effects, eventual consistency, and actual head mismatch to remain distinct. Only Presence.FOUND with a different revision establishes the authority conflict; a non-authoritative read must produce an UNKNOWN receipt. The current read-failure test at tests/test_forge.py:948-957 does not enter creation's pre/post snapshot-verification or known-reference reconciliation paths.

  3. The fake/reference receipt contract diverges for malformed immediate PR evidence. src/creatidy_kernel/adapters/fake_forge.py:412-427 calls _pull() without normalizing ValueError, while src/creatidy_kernel/adapters/forgejo.py:391-408 maps equivalent malformed producer evidence to Receipt(UNKNOWN, ...). Existing malformed-response coverage at tests/test_forge.py:256-276 is Forgejo-only; the shared test at tests/test_forge.py:226-240 corrupts stored state only after an accepted creation. This violates the explicit shared fake/reference required-operation/receipt conformance criterion and can make controller behavior differ by adapter.

Non-blocking Suggestions

None.

Questions for the Author

None. The issue and owner decision specify the required authority model sufficiently.

Security / Privacy Notes

No credential disclosure, merge authority, deploy authority, ambient Git configuration use, or cross-origin redirect behavior was found in the reviewed diff. The first blocking issue is an authority/provenance gap in the snapshot namespace rather than a content-SHA mismatch.

Test / Documentation Gaps

Add shared fake/reference cases for: same-SHA pre-existing snapshot contention on first delivery; unavailable snapshot reads before POST, after POST, and during known-reference reconciliation; malformed immediate PR creation evidence. No documentation-only correction can replace these behavioral fixes.

Follow-Up Candidates

None. These are required issue #9 acceptance work, not distinct follow-ups.

Overall Recommendation

REQUEST_CHANGES

Three blocking findings remain at exact head cda6861c15b567de683b81cb6989826f3cdd9bd7. The review was read-only; PR code was not executed in the reviewer context. Exact-head Forgejo CI reports success, but the passing suite currently does not detect these authority/recovery defects.

Persisted review-state limitation: Forgejo rejected formal REQUEST_CHANGES because the authenticated reviewer is the PR author. This review is therefore stored as COMMENT; the substantive recommendation remains REQUEST_CHANGES and is not a formal approval.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** issue-9-forge-port -> develop **Frozen head:** `cda6861c15b567de683b81cb6989826f3cdd9bd7` **Linked issue:** #9 `[K5] Forge port and Forgejo adapter` ### Summary The port and adapter preserve the intended inward dependency direction, opaque qualified references, conditional Git effects, exact PR-head verification, base repository/branch identity, and absence of merge/deploy capability. Exact-head Forgejo workflow run 1973 succeeded. Three blocking issues remain in snapshot authority/recovery and fake/reference conformance. ### Intent / Acceptance Evidence | Issue #9 criterion | Evidence at frozen head | Result | | --- | --- | --- | | Shared fake/Forgejo required-operation and receipt conformance | Parameterized coverage is extensive, but immediate malformed PR evidence raises from the fake while Forgejo returns `UNKNOWN` | Blocked | | Opaque qualified identities and adapter-local Forgejo semantics | `core/forge.py`, `forge_refs.py`, binding and codec tests | Satisfied | | Distinct pagination, inaccessible/absent/unknown, stale head, unsupported capability, unknown effects | General observation coverage exists, but snapshot verification converts unavailable reads into `ForgeConflict` | Blocked | | Exact accepted SHA through a write-once snapshot created absent-to-exact and isolated through POST/verification | Snapshot lock and exact-SHA checks exist, but a fresh delivery adopts a pre-existing same-SHA snapshot without executing absent-target CAS | Blocked | | Base authority is repository plus target branch identity; observed SHA is provenance | PR response/reconciliation validate base ref identity and receipts retain observed base SHA; base movement test exists | Satisfied | | No merge; stale/moving head, contention, base movement, bounded input cases | No merge capability and broad synthetic coverage | Partially satisfied pending blockers | ### Architecture / Information Flow `adapters -> ports -> core` is preserved. Forgejo payload parsing remains at the adapter boundary, and the core owns normalized references, observations, effects, and receipts. The optional live transports are injected and are not invoked autonomously. ### Blocking Issues 1. **A fresh PR delivery adopts a pre-existing same-SHA snapshot instead of proving absent-to-exact creation.** In `src/creatidy_kernel/adapters/forgejo.py:362-368` and `src/creatidy_kernel/adapters/fake_forge.py:389-393`, an existing snapshot is accepted whenever its current SHA equals the authorized candidate, so `compare_and_push(..., expected=None, ...)` is skipped. The isolation guard prevents mutation while held, but it does not prove that this delivery created the ref or that the existing ref is Kernel-controlled. This contradicts issue #9 and the PR description's explicit absent-to-exact conditional-creation requirement. `tests/test_forge.py:426-444` currently pre-seeds the ref and expects a first delivery to adopt it, encoding the unsafe behavior. A first delivery must require absent-target CAS and treat any pre-existing ref as contention; uncertain later delivery must remain reconciliation-only and must not POST without a trusted known PR reference. 2. **Unavailable snapshot verification reads are misclassified as immutable authority conflicts.** `src/creatidy_kernel/adapters/forgejo.py:375-376`, `402-403`, and `442-443` compare only `.revision`. `UNKNOWN` or `INACCESSIBLE` observations have `revision=None`, so transient, permission-limited, or eventually consistent reads raise `ForgeConflict` just like a positively observed mismatched SHA. Issue #9 explicitly requires unavailable reads, unknown effects, eventual consistency, and actual head mismatch to remain distinct. Only `Presence.FOUND` with a different revision establishes the authority conflict; a non-authoritative read must produce an `UNKNOWN` receipt. The current read-failure test at `tests/test_forge.py:948-957` does not enter creation's pre/post snapshot-verification or known-reference reconciliation paths. 3. **The fake/reference receipt contract diverges for malformed immediate PR evidence.** `src/creatidy_kernel/adapters/fake_forge.py:412-427` calls `_pull()` without normalizing `ValueError`, while `src/creatidy_kernel/adapters/forgejo.py:391-408` maps equivalent malformed producer evidence to `Receipt(UNKNOWN, ...)`. Existing malformed-response coverage at `tests/test_forge.py:256-276` is Forgejo-only; the shared test at `tests/test_forge.py:226-240` corrupts stored state only after an accepted creation. This violates the explicit shared fake/reference required-operation/receipt conformance criterion and can make controller behavior differ by adapter. ### Non-blocking Suggestions None. ### Questions for the Author None. The issue and owner decision specify the required authority model sufficiently. ### Security / Privacy Notes No credential disclosure, merge authority, deploy authority, ambient Git configuration use, or cross-origin redirect behavior was found in the reviewed diff. The first blocking issue is an authority/provenance gap in the snapshot namespace rather than a content-SHA mismatch. ### Test / Documentation Gaps Add shared fake/reference cases for: same-SHA pre-existing snapshot contention on first delivery; unavailable snapshot reads before POST, after POST, and during known-reference reconciliation; malformed immediate PR creation evidence. No documentation-only correction can replace these behavioral fixes. ### Follow-Up Candidates None. These are required issue #9 acceptance work, not distinct follow-ups. ### Overall Recommendation `REQUEST_CHANGES` Three blocking findings remain at exact head `cda6861c15b567de683b81cb6989826f3cdd9bd7`. The review was read-only; PR code was not executed in the reviewer context. Exact-head Forgejo CI reports success, but the passing suite currently does not detect these authority/recovery defects. **Persisted review-state limitation:** Forgejo rejected formal `REQUEST_CHANGES` because the authenticated reviewer is the PR author. This review is therefore stored as `COMMENT`; the substantive recommendation remains `REQUEST_CHANGES` and is not a formal approval.
fix: require atomic snapshot creation for PR effects
All checks were successful
ci / check (pull_request) Successful in 31s
4f412d8022
kilo left a comment

PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22)

Repository: Creatidy/creatidy-kernel
Branch: issue-9-forge-port -> develop
Frozen head: 4f412d8022e4f1f39a1bcebf3f6afb17a89a31fa
Linked issue: #9

Summary

Fresh, read-only whole-PR review of the exact frozen head found no blocking defects. The provider-neutral core contract, Forgejo reference adapter, bounded transports, fake/reference conformance coverage, and documentation changes align with issue #9 and ADRs 0003/0004/0005. Exact-head CI run #1982 succeeded.

Intent / Acceptance Evidence

Acceptance area Evidence Assessment
Shared conformance tests/test_forge.py parameterizes fake and Forgejo adapters, including unavailable reads, binding, pagination, malformed evidence, receipts, and effects. Met offline
Opaque identities Core retains qualified opaque Reference values; Forgejo parsing/validation remains at the adapter boundary. Met
Safe observations Pagination, inaccessible/absent/unknown, malformed evidence, and eventual-consistency behavior fail closed. Met
Exact PR head First delivery creates the deterministic snapshot by absent-target atomic Git CAS, posts that snapshot rather than the mutable branch, and verifies response plus final head. Met
Isolation gate pr is unsupported before writes unless a binding-matched external SnapshotIsolation advertises write-once enforcement. Met
Base authority Repository plus base-branch identity is validated; observed base SHA is retained as receipt provenance and base movement alone is not authority conflict. Met
Uncertain replay Retry without a known durable PR reference remains UNKNOWN; known-reference reconciliation revalidates exact head, base identity, body, title, snapshot, and repository. Met
No merge/deploy The effect model and transports expose only branch, push, and PR actions. Met

Architecture / Information Flow

Dependency direction remains adapters -> ports -> core. Forgejo payloads and transport details do not leak into core, external success is normalized into domain receipts, and reads or malformed evidence cannot manufacture authority or acceptance.

Blocking Issues

None.

Non-blocking Suggestions

  • Low, documentation: README.md:16-18 says the included HTTPS and conditional Git transports can perform authorized branch, push, and PR effects when configured, but PR capability additionally requires a separately supplied, binding-matched write-once SnapshotIsolation; no live isolation provider ships here. Clarifying that prerequisite would prevent integrators from expecting the bundled transports alone to enable pr.

Questions for the Author

None.

Security / Privacy Notes

The exact-head snapshot path fails closed without externally enforced isolation. No merge/deploy authority, secret material, private dependency, or live provider requirement was introduced.

Test / Documentation Gaps

No blocking test gap. No live Forgejo integration was run; offline shared conformance and exact-head CI are the available evidence. The README wording above is the only identified documentation gap.

Follow-Up Candidates

None. The README clarification is small and may be handled directly without a separate issue.

Overall Recommendation

APPROVE

No blocking findings for frozen HEAD 4f412d8022e4f1f39a1bcebf3f6afb17a89a31fa.

Forgejo identity limitation: formal APPROVED submission was rejected because the authenticated account is treated as the PR author. This persisted COMMENT preserves the substantive verdict but is not a formal approval.

## PR Review: [K5] Add bounded Forge port and Forgejo reference adapter (#22) **Repository:** Creatidy/creatidy-kernel **Branch:** `issue-9-forge-port` -> `develop` **Frozen head:** `4f412d8022e4f1f39a1bcebf3f6afb17a89a31fa` **Linked issue:** #9 ### Summary Fresh, read-only whole-PR review of the exact frozen head found no blocking defects. The provider-neutral core contract, Forgejo reference adapter, bounded transports, fake/reference conformance coverage, and documentation changes align with issue #9 and ADRs 0003/0004/0005. Exact-head CI run #1982 succeeded. ### Intent / Acceptance Evidence | Acceptance area | Evidence | Assessment | | --- | --- | --- | | Shared conformance | `tests/test_forge.py` parameterizes fake and Forgejo adapters, including unavailable reads, binding, pagination, malformed evidence, receipts, and effects. | Met offline | | Opaque identities | Core retains qualified opaque `Reference` values; Forgejo parsing/validation remains at the adapter boundary. | Met | | Safe observations | Pagination, inaccessible/absent/unknown, malformed evidence, and eventual-consistency behavior fail closed. | Met | | Exact PR head | First delivery creates the deterministic snapshot by absent-target atomic Git CAS, posts that snapshot rather than the mutable branch, and verifies response plus final head. | Met | | Isolation gate | `pr` is unsupported before writes unless a binding-matched external `SnapshotIsolation` advertises write-once enforcement. | Met | | Base authority | Repository plus base-branch identity is validated; observed base SHA is retained as receipt provenance and base movement alone is not authority conflict. | Met | | Uncertain replay | Retry without a known durable PR reference remains `UNKNOWN`; known-reference reconciliation revalidates exact head, base identity, body, title, snapshot, and repository. | Met | | No merge/deploy | The effect model and transports expose only branch, push, and PR actions. | Met | ### Architecture / Information Flow Dependency direction remains `adapters -> ports -> core`. Forgejo payloads and transport details do not leak into core, external success is normalized into domain receipts, and reads or malformed evidence cannot manufacture authority or acceptance. ### Blocking Issues None. ### Non-blocking Suggestions - Low, documentation: `README.md:16-18` says the included HTTPS and conditional Git transports can perform authorized branch, push, and PR effects when configured, but PR capability additionally requires a separately supplied, binding-matched write-once `SnapshotIsolation`; no live isolation provider ships here. Clarifying that prerequisite would prevent integrators from expecting the bundled transports alone to enable `pr`. ### Questions for the Author None. ### Security / Privacy Notes The exact-head snapshot path fails closed without externally enforced isolation. No merge/deploy authority, secret material, private dependency, or live provider requirement was introduced. ### Test / Documentation Gaps No blocking test gap. No live Forgejo integration was run; offline shared conformance and exact-head CI are the available evidence. The README wording above is the only identified documentation gap. ### Follow-Up Candidates None. The README clarification is small and may be handled directly without a separate issue. ### Overall Recommendation APPROVE No blocking findings for frozen HEAD `4f412d8022e4f1f39a1bcebf3f6afb17a89a31fa`. Forgejo identity limitation: formal `APPROVED` submission was rejected because the authenticated account is treated as the PR author. This persisted `COMMENT` preserves the substantive verdict but is not a formal approval.
adrian.tkacz deleted branch issue-9-forge-port 2026-09-26 21:32:20 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Creatidy/creatidy-kernel!22
No description provided.