Files
Charon/docs/plans/propagation_workflow_update.md
GitHub Actions 3169b05156 fix: skip incomplete system log viewer tests
- Marked 12 tests as skip pending feature implementation
- Features tracked in GitHub issue #686 (system log viewer feature completion)
- Tests cover sorting by timestamp/level/method/URI/status, pagination controls, filtering by text/level, download functionality
- Unblocks Phase 2 at 91.7% pass rate to proceed to Phase 3 security enforcement validation
- TODO comments in code reference GitHub #686 for feature completion tracking
- Tests skipped: Pagination (3), Search/Filter (2), Download (2), Sorting (1), Log Display (4)
2026-02-09 21:55:55 +00:00

5.2 KiB

Plan: Refine Propagation Workflow to Enforce Strict Hierarchy (Pittsburgh Model)

1. Introduction

This plan outlines the update of the .github/workflows/propagate-changes.yml workflow. The goal is to enforce a strict hierarchical propagation strategy ("The Pittsburgh Model") where changes flow downstream from main to development, and then from development to leaf branches (feature/*, hotfix/*). This explicitly prevents "loop-backs" and direct updates from main to feature branches.

2. Methodology & Rules

The Pittsburgh Model (Strict Hierarchy):

  1. Rule 1 (The Ohio River): main ONLY propagates to development.

    • Logic: main is the stable release branch. Changes here (hotfixes, releases) must flow into development first.
    • Constraint: main must NEVER propagate directly to feature/* or hotfix/*.
  2. Rule 2 (The Point): development is the ONLY branch that propagates to leaf branches.

    • Logic: development is the source of truth for active work. It aggregates main changes plus ongoing development.
    • Targets: feature/* and hotfix/*.
  3. Rule 3 (Loop Prevention): Determine the "source" PR to prevent re-propagation.

    • Problem: When feature/A merges into development, we must not open a PR from development back to feature/A.
    • Mechanism: Identify the source branch of the commit triggering the workflow and exclude it from targets.

3. Workflow Design

3.1. Branching Strategy Logic

Trigger Branch Source Target(s) Logic
main main development Create PR main -> development
development development feature/*, hotfix/* Create PR development -> [leaf] (Excluding changes source)
feature/* - - No action (Triggers CI only)
hotfix/* - - No action (Triggers CI only)

3.2. Logic Updates Needed

A. Strict Main Enforcement

  • Current logic likely does this, but we will explicitly verify if (currentBranch === 'main') { propagate('development'); } and nothing else.

B. Development Distribution & Hotfix Inclusion

  • Update the branch listing logic to find both feature/* AND hotfix/* branches.
  • Current code only looks for feature/*.

C. Loop Prevention (The "Source Branch" Check)

  • Trigger: Script runs on push to development.
  • Action:
    1. Retrieve the Pull Request associated with the commit sha using the GitHub API.
    2. If a merged PR exists for this commit, extract the source branch name (head.ref).
    3. Exclude this source branch from the list of propagation targets.

3.3. Technical Implementation Details

  • File: .github/workflows/propagate-changes.yml
  • Action: actions/github-script

Pseudo-Code Update:

// 1. Get current branch
const branch = context.ref.replace('refs/heads/', '');

// 2. Rule 1: Main -> Development
if (branch === 'main') {
    await createPR('main', 'development');
    return;
}

// 3. Rule 2: Development -> Leafs
if (branch === 'development') {
    // 3a. Identify Source (Rule 3 Loop Prevention)
    // NOTE: This runs on push, so context.sha is the commit sha.
    let excludedBranch = null;
    try {
        const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({
            owner: context.repo.owner,
            repo: context.repo.repo,
            commit_sha: context.sha,
        });
        // Find the PR that was merged
        const mergedPr = prs.data.find(pr => pr.merged_at);
        if (mergedPr) {
             excludedBranch = mergedPr.head.ref;
             core.info(`Commit derived from merged PR #${mergedPr.number} (Source: ${excludedBranch}). Skipping back-propagation.`);
        }
    } catch (e) {
        core.info('Could not check associated PRs: ' + e.message);
    }

    // 3b. Find Targets
    const branches = await github.paginate(github.rest.repos.listBranches, {
        owner: context.repo.owner,
        repo: context.repo.repo,
    });

    const targets = branches
        .map(b => b.name)
        .filter(b => (b.startsWith('feature/') || b.startsWith('hotfix/')))
        .filter(b => b !== excludedBranch); // Exclude source

    // 3c. Propagate
    core.info(`Propagating to ${targets.length} branches: ${targets.join(', ')}`);
    for (const target of targets) {
        await createPR('development', target);
    }
}

4. Implementation Steps

  1. Refactor main logic: Ensure it returns immediately after propagating to development to prevent any fall-through.
  2. Update development logic:
    • Add hotfix/ to the filter regex.
    • Implement the listPullRequestsAssociatedWithCommit call to identify the exclusion.
    • Apply the exclusion to the target list.
  3. Verify Hierarchy:
    • Confirm no path exists for main -> feature/*.

5. Acceptance Criteria

  • Push to main creates a PR ONLY to development.
  • Push to development creates PRs to all downstream feature/* AND hotfix/* branches.
  • Push to development (caused by merge of feature/A) does NOT create a PR back to feature/A.
  • A hotfix merged to main flows: main -> development, then development -> hotfix/active-work (if any exist).