os-sdlc: code-write skill — mechanical code writes (ast-grep rewrites + new-file Write) with post-apply lint hook #337

Closed
opened 2026-08-10 19:05:55 +00:00 by jared · 3 comments
Owner

Goal

Create an ast-grep write skill (working name code-rewrite, sibling of code-probe in os-sdlc) that applies mechanical code changes structurally — probe, rewrite, preview, apply, lint — without Read/Write on the target file. Proven end-to-end on #316 (commit a1710ed): investigation and both edits ran ast-grep-only; the two gotchas below were hit and are now the skill's reason to exist.

Steps (skill body in pseudocode-skill style)

Cross-cutting preamble: mechanical transforms only — if choosing the replacement requires deciding behavior, stop and emit the matched slices plus the decision question (same gate as code-probe's rewrite.md). Never Read the target file; matched slices + preview diffs are the only context.

1. Name and scaffold

  • Confirm the name before creating anything
    • Read plugins/cc-architect/references/conventions/cc-os-naming.md -> noun-first kebab-case, no name: in frontmatter
  • Write frontmatter per the IS/WHEN split
    • description: artifact-category-first ("A rewrite procedure that turns a mechanical change decision into an applied, linted, test-verified diff …")
    • when_to_use: reader problem state, not a pipeline phase ("You have a settled mechanical change and want it applied structurally without loading the file")

2. Locate the target structurally

  • Reuse code-probe's patterns to find the exact nodes
    • ast-grep run --lang ruby -p '<pattern>' <path> — definition and call-site shapes from code-probe's cheatsheet
    • 0 hits -> your tree didn't occur; re-derive the pattern in the playground, never fall back to Read

3. Draft the rewrite

  • Default to the inline form — no rule file
    • ast-grep run --lang ruby -p '<pattern>' --rewrite '<fix>' <path> (no -U = preview)
    • needs kind:, relational rules, or context:/selector: -> only then write a YAML rule and use scan
  • Author multi-line fix text relative to the match
    • every continuation line gets the matched node's indentation prefixed; a same-level statement gets ZERO leading spaces in the fix text (gotcha 1: double-indent)

4. Preview, apply, lint

  • Preview and judge the diff before touching anything
    • the ± 3-line preview IS the review context; wrong shape -> back to step 3
  • Apply
    • same command with -U
  • Run the post-apply lint step unconditionally
    • Ruby: rubocop -a --only Layout/TrailingWhitespace <file>; other languages: sed -i 's/[ \t]*$//' <file>
    • gotcha 2: ast-grep stamps the indentation prefix onto blank lines in every multi-line fix — structural, happens every time, this one step corrects it forever
    • candidate: make this a PostToolUse hook on the skill rather than a step, so it cannot be skipped

5. Verify

  • Run the project's test suite and the linter on the changed file only
    • new offenses beyond pre-existing classes -> fix or surface; suite red -> revert via git, report

Reference files

  • Move or share rewrite.md (currently under code-probe's references/) so this skill owns the rewrite mechanics doc; code-probe keeps pointing at it for the fix-preview-as-evidence use
  • Inject references via dynamic context injection the same way code-probe does (see the frontmatter-edit context-injection pattern, commit 1e90e5c), instead of telling the reader to go read files
  • Add the inline --rewrite form to rewrite.md — it currently documents only the YAML path

Acceptance

  • Skill passes os-aidd-lint Tier 1 and the pseudocode-skill lint rules (SecondBrain howto/writing-pseudocode-style-skills.md)
  • A dry run on a two-site mechanical change completes with zero Read/Write/Edit calls on the target file and zero new lint offenses
## Goal Create an ast-grep write skill (working name `code-rewrite`, sibling of `code-probe` in os-sdlc) that applies mechanical code changes structurally — probe, rewrite, preview, apply, lint — without Read/Write on the target file. Proven end-to-end on #316 (commit a1710ed): investigation and both edits ran ast-grep-only; the two gotchas below were hit and are now the skill's reason to exist. ## Steps (skill body in pseudocode-skill style) Cross-cutting preamble: mechanical transforms only — if choosing the replacement requires deciding behavior, stop and emit the matched slices plus the decision question (same gate as code-probe's rewrite.md). Never Read the target file; matched slices + preview diffs are the only context. ### 1. Name and scaffold - Confirm the name before creating anything - Read `plugins/cc-architect/references/conventions/cc-os-naming.md` -> noun-first kebab-case, no `name:` in frontmatter - Write frontmatter per the IS/WHEN split - `description`: artifact-category-first ("A rewrite procedure that turns a mechanical change decision into an applied, linted, test-verified diff …") - `when_to_use`: reader problem state, not a pipeline phase ("You have a settled mechanical change and want it applied structurally without loading the file") ### 2. Locate the target structurally - Reuse code-probe's patterns to find the exact nodes - `ast-grep run --lang ruby -p '<pattern>' <path>` — definition and call-site shapes from code-probe's cheatsheet - 0 hits -> your tree didn't occur; re-derive the pattern in the playground, never fall back to Read ### 3. Draft the rewrite - Default to the inline form — no rule file - `ast-grep run --lang ruby -p '<pattern>' --rewrite '<fix>' <path>` (no `-U` = preview) - needs `kind:`, relational rules, or `context:`/`selector:` -> only then write a YAML rule and use `scan` - Author multi-line fix text relative to the match - every continuation line gets the matched node's indentation prefixed; a same-level statement gets ZERO leading spaces in the fix text (gotcha 1: double-indent) ### 4. Preview, apply, lint - Preview and judge the diff before touching anything - the ± 3-line preview IS the review context; wrong shape -> back to step 3 - Apply - same command with `-U` - Run the post-apply lint step unconditionally - Ruby: `rubocop -a --only Layout/TrailingWhitespace <file>`; other languages: `sed -i 's/[ \t]*$//' <file>` - gotcha 2: ast-grep stamps the indentation prefix onto blank lines in every multi-line fix — structural, happens every time, this one step corrects it forever - candidate: make this a PostToolUse hook on the skill rather than a step, so it cannot be skipped ### 5. Verify - Run the project's test suite and the linter on the changed file only - new offenses beyond pre-existing classes -> fix or surface; suite red -> revert via git, report ## Reference files - Move or share `rewrite.md` (currently under code-probe's references/) so this skill owns the rewrite mechanics doc; code-probe keeps pointing at it for the fix-preview-as-evidence use - Inject references via dynamic context injection the same way code-probe does (see the frontmatter-edit context-injection pattern, commit 1e90e5c), instead of telling the reader to go read files - Add the inline `--rewrite` form to rewrite.md — it currently documents only the YAML path ## Acceptance - Skill passes os-aidd-lint Tier 1 and the pseudocode-skill lint rules (SecondBrain `howto/writing-pseudocode-style-skills.md`) - A dry run on a two-site mechanical change completes with zero Read/Write/Edit calls on the target file and zero new lint offenses
jared changed title from os-sdlc: code-rewrite skill — ast-grep structural edits with post-apply lint to os-sdlc: code-write skill — mechanical code writes (ast-grep rewrites + new-file Write) with post-apply lint hook 2026-08-10 21:47:23 +00:00
Author
Owner

Grilling session 2026-08-10 — decision record (supersedes body where they differ):

  1. Name/scope: code-write — all mechanical code writes. Existing file → ast-grep structural rewrite (anchored insertions included); nonexistent file → single Write, no read-back; scaffolding generators compatible. Invariant: never Read the target.
  2. No escape hatch: anchor failure → error loudly with attempted patterns. Full-context-agent kick-out deferred until real.
  3. Lint hook: one PostToolUse script, two matchers — Bash commands containing ast-grep … -U, and Write of .rb files — running rubocop -a on changed files only (per-file config resolution; never repo-wide: root-config run currently shows ~3,500 autocorrectable offenses). Skill has no lint step; step-5 verify is the backstop.
  4. rewrite.md: moves to code-write's references/ (dynamic context injection), gains the inline --rewrite form; code-probe drops its pointer entirely.
  5. Probe/write boundary: code-probe classifies and hands off the six-line evidence brief; code-write drafts, previews, applies, verifies. code-probe step 5 loses the drafting sub-branch, keeps classification + judgment decision questions.
  6. TDD: fork on the brief's verdict — behavior-preserving rides the existing green suite; behavior-changing gets a failing test from test-writer first; code-write is the programmer's technique. No gates in the skill (ADR-0097); caller/Runner owns sequencing.
  7. Acceptance amended: zero Read/Edit on any target; Write permitted only for files that don't yet exist. Template remains code-probe SKILL.md (pseudocode-skill style, IS/WHEN frontmatter, os-aidd-lint Tier 1 clean).
Grilling session 2026-08-10 — decision record (supersedes body where they differ): 1. **Name/scope**: `code-write` — all mechanical code writes. Existing file → ast-grep structural rewrite (anchored insertions included); nonexistent file → single Write, no read-back; scaffolding generators compatible. Invariant: never Read the target. 2. **No escape hatch**: anchor failure → error loudly with attempted patterns. Full-context-agent kick-out deferred until real. 3. **Lint hook**: one PostToolUse script, two matchers — Bash commands containing `ast-grep … -U`, and Write of `.rb` files — running `rubocop -a` on changed files only (per-file config resolution; never repo-wide: root-config run currently shows ~3,500 autocorrectable offenses). Skill has no lint step; step-5 verify is the backstop. 4. **rewrite.md**: moves to code-write's references/ (dynamic context injection), gains the inline --rewrite form; code-probe drops its pointer entirely. 5. **Probe/write boundary**: code-probe classifies and hands off the six-line evidence brief; code-write drafts, previews, applies, verifies. code-probe step 5 loses the drafting sub-branch, keeps classification + judgment decision questions. 6. **TDD**: fork on the brief's verdict — behavior-preserving rides the existing green suite; behavior-changing gets a failing test from test-writer first; code-write is the programmer's technique. No gates in the skill (ADR-0097); caller/Runner owns sequencing. 7. **Acceptance amended**: zero Read/Edit on any target; Write permitted only for files that don't yet exist. Template remains code-probe SKILL.md (pseudocode-skill style, IS/WHEN frontmatter, os-aidd-lint Tier 1 clean).
Author
Owner

Work started 2026-08-10: building the code-write skill per the decision record comment above. Working directly on main in ~/dev/cc-os (no PR branch yet).

Work started 2026-08-10: building the code-write skill per the decision record comment above. Working directly on main in ~/dev/cc-os (no PR branch yet).
Author
Owner

Resolution

Done: Built the os-sdlc code-write skill: ast-grep structural rewrites with caller-intent create/transform branching, probe-pattern reuse, injected pattern catalog, preview-judged applies; moved rewrite.md to code-write; code-probe brief now emits matched patterns; added lint_changed.rb PostToolUse hook with tests. Verify step removed — testing/linting owned by hooks and the Runner state machine.

Evidence: Commit 471af272fc on main (jared/cc-os)

Follow-ups: none

Approved-by: jared, in-session review 2026-08-11

## Resolution **Done:** Built the os-sdlc code-write skill: ast-grep structural rewrites with caller-intent create/transform branching, probe-pattern reuse, injected pattern catalog, preview-judged applies; moved rewrite.md to code-write; code-probe brief now emits matched patterns; added lint_changed.rb PostToolUse hook with tests. Verify step removed — testing/linting owned by hooks and the Runner state machine. **Evidence:** Commit 471af272fc72a4e82f6266ae116d7028e10e4a77 on main (jared/cc-os) **Follow-ups:** none Approved-by: jared, in-session review 2026-08-11
jared closed this issue 2026-08-11 13:07:19 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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
jared/cc-os#337
No description provided.