Skip to content

Fix portfolio truth authority and attention classification#177

Closed
saagpatel wants to merge 14 commits into
mainfrom
codex/truth-authority-integrity
Closed

Fix portfolio truth authority and attention classification#177
saagpatel wants to merge 14 commits into
mainfrom
codex/truth-authority-integrity

Conversation

@saagpatel

Copy link
Copy Markdown
Owner

Summary

  • bind canonical portfolio truth publication to clean producer receipts
  • exclude backup, scratch, generated, nested-content, and linked-worktree false detections
  • stop weak context from manufacturing decision-needed status
  • move settled infrastructure lanes to manual-only while preserving explicit active exceptions

Verification

  • Ruff passed on changed Python surfaces
  • 132 focused tests passed
  • operator OS seam linter passed with no findings
  • canonical portfolio truth regenerated for 181 projects
  • security coverage remains explicitly unknown at 0 scanned repositories

Boundaries preserved

  • no product, release, security, publication, or ambiguous lifecycle decision was inferred
  • Notion advisory context was carried forward because live Notion was unavailable
  • stale warehouse refresh remains outside this PR

saagpatel added 14 commits July 11, 2026 00:38
…ng constants

- Delete dead .weight class attributes from all analyzers; scorer.WEIGHTS
  is the single source of truth. New tests/test_scoring_weights.py asserts
  weights sum to 1.0, match the composed dimension set, and introspects
  analyzer modules so a dead weight attr cannot be silently reintroduced.
- Remove dead path_confidence parameter from build_risk_entry (accepted,
  never read) and its call sites.
- Make STALE_THRESHOLD_DAYS, GRADE_THRESHOLDS, COMPLETENESS_TIERS
  overridable via reserved scoring-profile keys; ScoringProfile carries
  overrides alongside flat weights so existing custom_weights callers and
  shipped profiles are byte-identical.
- Label the unscored description dimension via display_dimension() on
  operator-facing render surfaces; data fields keep raw dimension slugs.
…tale->parked moves to render

derived.registry_status was a pure synonym table over activity_status
(_registry_status_for: "parked" if stale else activity_status), forcing every
downstream function (build_risk_entry, _attention_state_for, validators,
renderers) to carry two parameters for one fact.

Traced the "archived" side door precisely: activity_status already produced
"archived" directly inside _activity_status_for (github_archived OR declared
lifecycle_state == "archived"), and registry_status only ever passed that
value through unchanged. The legacy registry file never fed the computation —
it only drove an advisory diff warning. So "archived" was a lifecycle fact
smuggled inside a recency observation, not a separate input.

- activity_status is now pure recency (active/recent/stale), computed without
  the archived shortcut.
- derived.archived: bool is the new first-class lifecycle field (github_archived
  or declared lifecycle_state == "archived"), passed independently to
  build_risk_entry, build_operating_path_entry, and _attention_state_for.
- The stale->parked relabel moved to one documented function,
  portfolio_truth_types.display_activity_status, used by every renderer/report
  surface that used to read registry_status for display (portfolio_truth_render,
  weekly_command_center, portfolio_context_recovery, the legacy-diff warning).
- Snapshot source_summary.registry_status_counts is replaced by
  activity_status_counts + archived_count. Schema bumped to 0.9.0; 0.8.0 added
  to LEGACY_SCHEMA_VERSIONS.
- portfolio_automation's ELIGIBLE_REGISTRY_STATUS/AutomationCandidate field
  renamed to activity_status; run_instructions_audit's archived filter now
  reads the boolean directly.

Behavior parity verified: risk tier, attention state, and displayed status are
unchanged for every repo. Full suite: 2881 passed, 2 skipped (matches main
baseline exactly). ruff check: clean.
…ting_path (read-compat kept)

Wires operating_path as a first-class normalized catalog field end-to-end
(loader, reconciler declared_values, scorecard program resolution, intent
alignment, catalog summaries) -- it was previously accepted by the resolver
but never actually populated from the catalog, so every entry resolved
through the intended_disposition fallback. Rewrites config/portfolio-catalog.yaml
and the example catalog (179 + 3 entries, identity value mapping) to declare
operating_path directly, repoints the scorecards.yaml archive-disposition rule
at the same field, and keeps intended_disposition as a deprecated read-compat
fallback (resolver, loader, scorecards, catalog validator) with a new loader
warning when an entry still declares it.
@saagpatel saagpatel marked this pull request as draft July 14, 2026 03:53
Comment thread src/report_enrichment.py

from src.portfolio_truth_types import truth_latest_path
from src.report_contracts import RiskLookupEntry, RiskPosture, TopElevatedEntry
from src.scorer import display_dimension


def test_analyzer_classes_do_not_declare_dead_weight_attributes() -> None:
import src.analyzers as analyzers_package
@saagpatel

Copy link
Copy Markdown
Owner Author

Superseded by dependency-ordered PRs #178 through #184. The original 14-commit/70-file PR was intentionally decomposed to preserve review scope and merge ancestry.

@saagpatel saagpatel closed this Jul 14, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bbd4103b02

ℹ️ 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".

Comment on lines +83 to +84
if _text(derived.get("activity_status")) not in ELIGIBLE_ACTIVITY_STATUS:
blockers.append("activity-status-not-eligible")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep archived repos out of automation candidates

Because activity_status is now pure recency, an archived project with recent local activity has derived.activity_status == "active" and derived.archived == true; this gate no longer checks the archived bit, so a trusted run can surface archived repos as bounded-automation candidates when they also have high path confidence and sufficient context. This regresses the previous registry_status == "archived" blocker and can route retired/read-only work into the automation lane.

Useful? React with 👍 / 👎.

Comment on lines +116 to +118
activity_status=display_activity_status(
project.derived.activity_status, archived=project.derived.archived
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude archived repos from context recovery targets

At this point the plan can label a target as archived, but the selection above still only requires project.derived.activity_status in {"active", "recent"}. Since archived is no longer encoded in activity_status, recently-touched archived projects with weak context become eligible and apply_context_recovery_plan may write managed context blocks into repos that should remain retired; add an explicit not project.derived.archived gate before creating targets.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants