Land the portfolio generation publisher - #248
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 59f3bc8f3a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if not artifacts: | ||
| raise PortfolioGenerationError("at least one generation artifact is required") |
There was a problem hiding this comment.
Require the canonical artifacts before advancing the pointer
The publish CLI accepts any nonempty artifact list, so an invocation that omits portfolio-truth.json or misspells one of the consumer-required names still replaces pointer.json successfully. Deployed consumers then resolve a generation that lacks the file they expect, despite publication reporting success; validate the required artifact names and their media/contract versions before constructing the manifest.
AGENTS.md reference: src/AGENTS.md:L17-L19
Useful? React with 👍 / 👎.
| if pre_pointer_check is not None: | ||
| pre_pointer_check() |
There was a problem hiding this comment.
Hold source validation through pointer replacement
When the security receipt collector or producer checkout changes concurrently, it can do so after pre_pointer_check() returns but before the later os.replace, allowing the pointer to publish a generation bound to evidence that is no longer current. The security receipt code already exposes a shared verified-binding lock for this purpose; the final validation needs to remain protected through the pointer commit rather than ending before it.
AGENTS.md reference: AGENTS.md:L65-L70
Useful? React with 👍 / 👎.
| ) -> dict[str, Any]: | ||
| if security.state != "fresh": | ||
| raise PortfolioGenerationError("security binding must be fresh") | ||
| if security.producer_commit != producer.commit: | ||
| raise PortfolioGenerationError("security and producer commits disagree") | ||
| if security.producer_repository != producer.repository: | ||
| raise PortfolioGenerationError("security and producer repositories disagree") | ||
| evaluated = _parse_instant(security.evaluated_at, field="security evaluated_at") | ||
| valid_until = _parse_instant(security.valid_until, field="security valid_until") |
There was a problem hiding this comment.
Validate provenance fields in the public publisher
Programmatic callers of publish_generation can supply a SecurityBinding with state="fresh" but a failed terminal state, malformed hashes, an uncontrolled terminal receipt, or invalid producer receipt metadata, and this function will still advance the canonical pointer because only freshness and repository/commit agreement are checked. Since resolve_current also does not validate these nested fields, the resulting false provenance remains accepted; enforce the same binding invariants used by the CLI loaders inside the publication boundary.
AGENTS.md reference: src/AGENTS.md:L5-L10
Useful? React with 👍 / 👎.
What
Lands the portfolio generation publisher that the deployed consumers already expect.
personal-ops (deployed 2026-08-29) reads portfolio truth from
~/.local/state/portfolio-generationswhenever a pointer exists there, and notion-operating-system main does the same. The publisher that writes those generations,portfolio_generation.py, existed only on a Codex branch in a work directory that never reached GitHub; the one generation on disk was published by hand from a dirty tree on 2026-08-30 and has been stale since. That staleness is why the nightly notion snapshot readback has failed since 2026-08-26.This PR is that branch (
codex/p11-i009-generation-contract-20260825, pushed as-is for durability) merged with main. No conflicts.Contents
src/github_repo_auditor/portfolio_generation.py:publish,verifyandswap-current-previousfor coherent, hash-bound portfolio generations under a pointer + releases layout.tests/test_portfolio_generation.py: 277 lines of contract coverage.Verification
uv run pytest: 3660 passed, 2 skippeduv run ruff check src tests: cleanPairs with
operator-scripts PR that wires
publishandverifyinto the nightlycom.d.portfolio-maintenancerun. Until both land and a new operator-scripts release is activated, the generation on disk stays stale.