Skip to content

Restore retrospective artifact copying parity #1031

Description

@tbhb-agent

Problem

Restore retrospective artifact copying parity after #331. The current Go implementation copies and counts a dangling extra link although the frozen shell warns and skips it (F1), recursively copies an extra of . into its own output although the shell refuses it (F2), and discards a failed report's output although shell redirection truncates the destination first and preserves partial output (F3).

This is CHILD-A, "Restore retrospective artifact copying parity". It owns parent AC6 and the extra-artifact behavior already merged from parent AC13. The base retains shared copying code; this child corrects its observable behavior without removing any existing test.

Acceptance criteria

  1. AC1: Given a readable transcript, when creation succeeds, then the artifact directory contains an independent transcript copy and the four canned reports with the expected contents; a linked transcript is copied as content.
  2. AC2: Given extra files, directories and links to existing artifacts, when creation succeeds, then their contents and nested link behavior match the frozen shell and the summary counts only copied extras. Given a nonexistent path or a dangling top-level link, creation warns "skipping missing artifact", leaves no copy for that extra, excludes it from the count and continues successfully.
  3. AC3: Given . or an extra directory that contains the retrospective destination, when creation runs, then it exits nonzero without recursively reproducing its output tree or printing a success summary, matching the frozen shell's refusal outcome. Dropped: the frozen shell copies with cp -R, and stock macOS cp and GNU cp disagree on a copy into itself, so the shell cannot define the expected result. AC6 states the outcome directly.
  4. AC4: Given any canned report that fails before output or after partial output, including an existing report destination, when creation runs, then that destination contains only the bytes emitted by the failing invocation, including an empty file when nothing was emitted. Previously completed reports remain available, later reports are not refreshed, creation exits nonzero, and no success summary is printed.
  5. AC5: Non-functional: given the child diff, when it is measured and verified with all existing and added tests, then it stays strictly below 800 code lines including generated copies, passes the existing coverage floors, and changes no gate, coverage exclusion or shell entry point.
  6. AC6: Given ., a directory that contains the retrospective destination, or a path reaching such a directory through a symlinked component, when creation runs, then it exits nonzero, prints a diagnostic that the artifact cannot be copied into itself, prints no success summary, and leaves exactly one transcript.jsonl in the artifact tree and no RETRO.md. The outcome is asserted directly, without comparing against shell output.

Scope and non-goals

In scope: parent AC6, merged AC13 and F1-F3, including regression tests for dangling extras and failed report files, and the AC6 test for self-containing directories. Preserve transcript-link dereferencing and the already-fixed absence of a redundant child-error diagnostic.

Non-goal: preflight rendering and subprocess-status parity, which belong to CHILD-B, "Restore retrospective preflight and exit-status parity"; activation #1002; removal #1003; report-engine changes; test removal or gate changes.

Approach

Correct missing-extra detection, reject recursive destination containment, and open each report destination before running that report so failure leaves its actual evidence. Reuse shared test fixtures and add artifact regression tests without moving or shortening the base tests. Shared reporting setup may change within this child; metadata buffering remains separate from report-file output.

The measured scratch candidate changes copyArtifact, preserve, and the report-command setup in internal/retro/new.go, and adds internal/retro/artifact_parity_test.go. Production delivery must establish the complete acceptance criteria, including the ordered effects on other reports; the sizing candidate is evidence of fit, not a published fix.

Dependencies

This work is blocked by the BASE-1 slice of #331, which carries the copying code this child corrects. Delivery order is BASE-1, then this artifact child, with CHILD-B after BASE-2 and this child. CHILD-B is blocked by both preceding deliveries. Activation #1002 must wait for all three and launcher #932. Removal #1003 follows activation and its N-2 warning window. Blocked on: the BASE-1 slice of #331. When a route exists, record that as a native blocked-by relation, not prose only. Filing step: once this child and CHILD-B both exist, add native blocked-by links from #1002 to each of them, since #1002 currently has native blockers only for #331 and #932. No new issue or dependency relationship is represented as already filed.

Size

S: measured scratch candidate 113 code lines, consisting of 108 added and 5 deleted code lines, including the complete new regression-test file; generated-copy changes: 0. Strictly below 800. No test was removed or shortened to fit. Remeasure the final diff after completing all acceptance checks. These scratch-candidate counts are planning figures and must not be copied into pull request descriptions.

Verify plan

  • AC1: retain TestRetroNew and TestRetroSymlinkedTranscript; compare transcript and canned-report contents against the unchanged shell on isolated fixtures.
  • AC2: retain file, directory, nested-link and missing-path assertions in TestRetroNew; add TestRetroDanglingExtra and comparisons for readable top-level links, warning text and counts.
  • AC3: struck, see AC6.
  • AC6: TestRetroSelfContainingExtra runs the Go engine for ., a containing directory, and a path through a symlinked component, and asserts nonzero status, the diagnostic, empty standard output, a single transcript.jsonl under the artifact tree and no RETRO.md. It does not run the shell for these cases.
  • AC4: add TestRetroReportFailureEvidence for all four reports with empty and partial output over existing files; assert exact retained bytes, nonzero status and no success summary. The candidate also asserts completed reports are refreshed and later destinations remain unchanged; compare those effects with the shell before delivery.
  • AC5: run go vet ./internal/retro ./internal/agentrepo, go test ./internal/retro ./internal/agentrepo, mise run lint-go, mise run check-generated-skills and mise run cover-go, which writes a fresh coverage profile on the delivery revision and then enforces the unchanged thresholds; AC5 is met only when all five pass on the exact delivery revision. A threshold check against an older profile does not count. Measure added and deleted code with scc 4.1.0, counting new files in full and any generated copies. Require the complete original test set and no gate changes.
  • Parent crosswalk: parent AC6 is covered by child AC2, AC6 and AC4. Parent AC13 is covered through child AC2 and AC6. Parent IDs stay unchanged. The base keeps parent AC11 for the artifact totals of a normal run, and this child adds only the extra-artifact counts under child AC2.
  • Record each AC as met, not met or deferred on the exact delivery revision. The scratch candidate passes the targeted Go tests; full acceptance, coverage and delivery gates are not claimed by this draft.

Risks

Containment checks must reject recursive copies without changing valid nested-link handling. Report failures intentionally leave truncated or partial files, so verification must inspect evidence on disk rather than only exit status. Undo: revert this child before activation; if CHILD-B has landed, revert it first. Retain the base and frozen entry points while acceptance remains incomplete.

Operator authority

None. No gate, owner path, coverage exclusion or shell entry point changes. The edit removes a shell comparison that cannot be deterministic across cp flavors and states the required outcome directly.

Activity

  1. tbhb-agent commented on Nov 1, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — artifact-parity child is bounded, with fresh coverage verification

    Checked

    Gaps

    None.

    Split

    The delivery order is base #331, CHILD-A, then CHILD-B; CHILD-B is blocked by both prior deliveries. Activation #1002 waits for the base, both children and #932; removal #1003 follows activation and the N-2 window. The draft correctly treats future dependency links as not yet filed and calls for native blocked-by links where the issues exist.

    Loosens: no

    Definition of done

    Planning coverage only; these checkboxes describe what the draft’s ACs and Verify plan cover, not completed delivery work.

    • D1 Verify each AC on the merged head and cite evidence in the PR’s Verification section.
    • D2 Independent review of the exact merged head and disposition of findings.
    • D3 Required checks pass; exact-head owner approval for any gate loosening.
    • D4 Required leak scans and per-post leak review are clean.
    • D5 Every commit, including the squash, is signed.
    • D6 Documentation matches the change, or the PR explains why there is no doc impact.
    • D7 Required Assisted-by trailers and PR provenance label are present.
    • D8 Size is bounded below 800 code lines, with final remeasurement required.
    • D9 Deferred work is filed and listed under Related in the PR.
    • D10 Merge closes the issue, the board shows Done, and the final Status comment names the merge commit.

    Round: 3 of 3

  2. added
    refinement:readyPassed the Definition of Ready; may be picked up
    refinement:activeA refinement round is open; not ready for implementation
    and removed
    refinement:readyPassed the Definition of Ready; may be picked up
    on Nov 1, 2026
  3. tbhb-agent commented on Nov 1, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — artifact corrections remain distinct from the creation base

    Checked

    • Diffed the proposed body against the live body; the live timestamp matches.
    • AC1–AC5 retain their IDs, with relevant checks for each. Copying, missing-extra handling and report failure evidence follow packages/agents/source/skills/analyze-session/shared/scripts/new-retro.sh:109, packages/agents/source/skills/analyze-session/shared/scripts/new-retro.sh:117 and packages/agents/source/skills/analyze-session/shared/scripts/new-retro.sh:112.
    • Removing AC1’s summary clause preserves combined coverage: BASE-1 AC11 remains the acceptance owner, with its Verify entry checking the successful summary and ordering. This child retains artifact contents and extra-count outcomes.
    • AC5 requires fresh coverage, unchanged floors, retained tests and final size measurement. The 113-line scratch estimate is plausible for the focused corrective scope.
    • Cosmetic stale wording: “once this child and CHILD-B both exist” should identify Restore retrospective artifact copying parity #1031 and Restore retrospective preflight and exit-status parity #1032, which already exist. The opening “after Port analyze-session's shell scripts to Python #331” could say “after BASE-1” to match Dependencies.

    Gaps

    None.

    Split

    BASE-1 supplies the copying implementation; this issue corrects dangling extras, recursive containment and failed-report evidence. #1032 follows both this issue and BASE-2. This is compatible with the other bodies’ delivery order.

    The approach changes the relevant copying and report-output operations directly, without speculative machinery.

    Loosens: no — only duplicate summary ownership is removed; BASE-1 AC11 and its Verify entry retain that guarantee.

    Owner-tier: no — the stated implementation and test paths require none of the listed protected paths.

    Definition of done

    The definition of done for #1031 (Feature). Tick each item and give its evidence.

    • D1 The author verified each AC on the merged head, and the Verification section of the pull request cites each AC ID with its evidence: the command and its result, or a link.
    • D2 An independent reviewer approved the exact merged head, and the author resolved each finding or filed it as a follow-up.
    • D3 The required checks pass on that head, and any loosened gate has the owner's approval of that exact head.
    • D4 The leak scans come back clean: gitleaks, devtools' check-leaks.sh with the repository's private-strings list, and the leak review of each post.
    • D5 The author signed each commit, the squash included.
    • D6 The docs match the change (README, docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why.
    • D7 The commits and the squash have Assisted-by trailers, and the pull request has the provenance:agent-assisted label.
    • D8 The pull request stays within the size limits, or the owner approved its exact head over the hard limit.
    • D9 The author files follow-ups and deferred findings as issues with at least a Problem and numbered AC and lists them under the Related section of the pull request. Any work left in the code links its issue.
    • D10 The merge closes the issue through a closing reference and the board shows Done. The final Status: comment names the merge commit.
    • devtools-1 mise run check is green, and so are the coverage floors mise run cover-go-check and mise run cover-py-check, which check doesn't run.
    • devtools-2 A change to skill sources under packages/agents/source/ commits what mise run generate-skills, then mise run skill-tokens, then apm install rewrite, in that order, and check-generated-skills (part of lint) passes.
    • devtools-3 A consumer-visible change (the vendored payload, a skill, a reusable workflow, a task or a pin) has its consumer steps in docs/agent-skills-migration.md or the adoption docs, or the PR's Verification says "No consumer impact" and why.

    Round: 1 of 3

  4. added
    refinement:readyPassed the Definition of Ready; may be picked up
    refinement:activeA refinement round is open; not ready for implementation
    and removed
    refinement:activeA refinement round is open; not ready for implementation
    refinement:readyPassed the Definition of Ready; may be picked up
    on Nov 1, 2026
  5. tbhb-agent commented on Nov 1, 2026

    @tbhb-agent
    ContributorAuthor

    Refinement: READY — deterministic self-copy outcomes replace the platform-dependent shell oracle

    Checked

    • origin identifies tbhb-dev/devtools. Diffed the proposed body against the live body. Earlier comments contain two READY verdicts and no NOT READY verdicts.
    • AC1, AC2, AC4, and AC5 retain their IDs and meaning. AC3 is struck with a reason; AC6 is the next number. There are six live criteria.
    • AC6 states observable outcomes: nonzero exit, diagnostic, no success summary, exactly one transcript.jsonl, and no RETRO.md. Its Verify entry tests the Go engine directly for ., a containing directory, and a path through a symlinked component, without shell comparison.
    • The origin/main tree does not contain internal/retro/new.go or the proposed test file, so there is no implementation there to inspect. The Verify plan keeps shell comparisons for AC1, AC2, and AC4, where shell behavior defines the stated parity outcomes.
    • Parent Port analyze-session's shell scripts to Python #331 assigns parent AC6 to CHILD-A for extra artifacts, self-containing extras, and failed-report evidence; merged AC13’s extra-artifact behavior is also covered by AC2 and AC6. No parent outcome loses an owner. Restore retrospective preflight and exit-status parity #1032 assigns artifact copying and report-file evidence to CHILD-A; Switch analyze-session to the Go retro command with the frozen scripts as fallback #1002 waits for both parity fixes. Their references do not contradict the new AC6. The struck AC3 is referenced only in its strike and the Verify pointer.
    • No named owner-tier path is in scope. Operator authority: None remains valid.

    Gaps

    None.

    Split

    Linux and macOS support symlinked paths; Windows can represent them with symlinks or junctions, subject to fixture setup. The . and symlinked-component cases are well-defined by AC6’s direct Go outcome, independent of cp behavior. The stated outcomes can hold on all three operating systems if tests create and resolve the symlinked component there.

    Loosens: no — AC6 removes the shell-parity requirement but specifies deterministic refusal and additional observable outcomes, preserving AC3’s safety property.

    Owner-tier: no

    Definition of done

    The definition of done for #1031 (Feature). Tick each item and give its evidence.

    • D1 The author verified each AC on the merged head, and the Verification section of the pull request cites each AC ID with its evidence: the command and its result, or a link.
    • D2 An independent reviewer approved the exact merged head, and the author resolved each finding or filed it as a follow-up.
    • D3 The required checks pass on that head, and any loosened gate has the owner's approval of that exact head.
    • D4 The leak scans come back clean: gitleaks, devtools' check-leaks.sh with the repository's private-strings list, and the leak review of each post.
    • D5 The author signed each commit, the squash included.
    • D6 The docs match the change (README, docs/, CHANGELOG or help text), or the Verification section says "No doc impact" and why.
    • D7 The commits and the squash have Assisted-by trailers, and the pull request has the provenance:agent-assisted label.
    • D8 The pull request stays within the size limits, or the owner approved its exact head over the hard limit.
    • D9 The author files follow-ups and deferred findings as issues with at least a Problem and numbered AC and lists them under the Related section of the pull request. Any work left in the code links its issue.
    • D10 The merge closes the issue through a closing reference and the board shows Done. The final Status: comment names the merge commit.
    • devtools-1 mise run check is green, and so are the coverage floors mise run cover-go-check and mise run cover-py-check, which check doesn't run.
    • devtools-2 A change to skill sources under packages/agents/source/ commits what mise run generate-skills, then mise run skill-tokens, then apm install rewrite, in that order, and check-generated-skills (part of lint) passes.
    • devtools-3 A consumer-visible change (the vendored payload, a skill, a reusable workflow, a task or a pin) has its consumer steps in docs/agent-skills-migration.md or the adoption docs, or the PR's Verification says "No consumer impact" and why.

    Round: 1 of 3

  6. added
    refinement:readyPassed the Definition of Ready; may be picked up
    and removed
    refinement:activeA refinement round is open; not ready for implementation
    on Nov 1, 2026
  7. moved this from Backlog to In progress in devtoolson Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    refinement:readyPassed the Definition of Ready; may be picked up

    Fields

    Priority

    None yet

    Work type

    None yet

    Projects

    • Status
      In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions