forgedock 1.8.1 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/engine/admission.mjs +57 -1
- package/bin/engine/admission.test.mjs +60 -0
- package/commands/analytics.md +3 -1
- package/commands/audit-agents.md +3 -1
- package/commands/audit.md +10 -8
- package/commands/autopilot.md +16 -14
- package/commands/ci-audit.md +4 -2
- package/commands/cleanup.md +1 -1
- package/commands/deploy-pr.md +31 -11
- package/commands/geo-audit.md +3 -1
- package/commands/incident-response.md +3 -1
- package/commands/issue.md +4 -2
- package/commands/milestone.md +8 -6
- package/commands/orchestrate/config.md +2 -2
- package/commands/orchestrate/phase-1-resolve.md +56 -12
- package/commands/orchestrate/phase-3-dependency.md +15 -5
- package/commands/orchestrate/phase-4-execution.md +593 -78
- package/commands/orchestrate/phase-5-cleanup.md +4 -2
- package/commands/orchestrate/phase-6-report.md +7 -3
- package/commands/orchestrate.md +13 -9
- package/commands/pipeline-health/phase-4-proposals.md +6 -4
- package/commands/pipeline-health/phase-5-report.md +4 -2
- package/commands/pipeline-health.md +9 -7
- package/commands/pipeline-resume.md +4 -2
- package/commands/qa-sweep.md +3 -1
- package/commands/quality-gate.md +4 -3
- package/commands/recover-orphans.md +172 -13
- package/commands/review-pr-agents.md +6 -4
- package/commands/review-pr-staging.md +13 -5
- package/commands/review-pr.md +187 -19
- package/commands/rollback.md +3 -1
- package/commands/security-audit.md +3 -1
- package/commands/signal-planner.md +6 -4
- package/commands/sync-ecosystem.md +4 -2
- package/commands/test-gate.md +4 -2
- package/commands/upgrade-deps.md +3 -1
- package/commands/validate.md +3 -1
- package/commands/work-on/build/architect.md +81 -19
- package/commands/work-on/build/context.md +61 -18
- package/commands/work-on/build/implement.md +62 -18
- package/commands/work-on/build/validate.md +240 -60
- package/commands/work-on/build.md +344 -146
- package/commands/work-on/close.md +617 -246
- package/commands/work-on/decompose.md +239 -41
- package/commands/work-on/investigate.md +377 -61
- package/commands/work-on/remediate.md +42 -9
- package/commands/work-on/review.md +240 -29
- package/commands/work-on-monolithic.md +48 -7
- package/commands/work-on.md +180 -2014
- package/docs/CODEX.md +2 -0
- package/docs/CONFIG.md +11 -0
- package/package.json +1 -1
- package/scripts/build-spec-graph.mjs +11 -4
- package/scripts/check-spec-markers.sh +11 -1
- package/scripts/checkbox-sections.test.sh +8 -23
- package/scripts/extract-affected-files.sh +5 -2
- package/scripts/extract-affected-files.test.sh +27 -0
- package/scripts/forge-root.test.sh +176 -0
- package/scripts/is-docs-only.sh +31 -0
- package/scripts/is-docs-only.test.sh +64 -0
- package/scripts/lint-dispatch-prompt.sh +217 -0
- package/scripts/lint-dispatch-prompt.test.sh +307 -0
- package/scripts/validate-spec-graph.sh +3 -2
- package/scripts/verify-phase-trail.sh +282 -0
- package/scripts/verify-phase-trail.test.sh +298 -0
- package/scripts/wait-ci-green.sh +98 -0
- package/scripts/wait-ci-green.test.sh +99 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Pre-implementation architecture planning — traces ALL affected code paths and produces an ordered implementation plan before any code is written
|
|
3
|
-
argument-hint: "
|
|
3
|
+
argument-hint: "{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG} --repo-path {WORKTREE} --files \"<space-separated>\""
|
|
4
|
+
context: fork
|
|
4
5
|
---
|
|
5
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
7
8
|
|
|
8
9
|
# work-on/build/architect — Multi-Path Implementation Planning
|
|
9
10
|
|
|
10
|
-
**
|
|
11
|
+
> **Skill Name Resolution (forked phase)**: `{FORGE_SKILL_PREFIX}` is the namespace this skill itself was invoked under — invoked as `forgedock:work-on:…` → `forgedock:` (nesting `:`); as `work-on:…` → empty (`install.sh`); as `forge-work-on-…` → `forge-` (Codex, nesting `-`); OpenCode → empty with `-` nesting. Confirm the target name in the available-skills list before calling it. A forked phase receives no resolved value from its caller; never guess, and if the target skill is not listed return BLOCKED "skill not found: <name>".
|
|
12
|
+
|
|
13
|
+
**Invoked by**: `work-on:build` (phase B4), between Context Gathering and Implement, via `Skill()`. Runs in its own forked context: you see only this spec and your args — re-read all other state from GitHub/git.
|
|
11
14
|
**Time budget**: Max 3 minutes. Skip any file read that times out.
|
|
12
|
-
**Output**: Post `<!-- FORGE:ARCHITECT -->` comment on the issue, then
|
|
15
|
+
**Output**: Post `<!-- FORGE:ARCHITECT -->` comment on the issue, then print exactly one `ARCHITECT_RESULT:` block (see Result Block) as your final reply. Every exit path — success, partial, skip, resume-complete, blocked — prints it.
|
|
13
16
|
|
|
14
17
|
**Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet", `effort: xhigh` (deep tier — full code-path tracing, multi-file architecture planning). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` only on Claude Code >= 2.1.154.
|
|
15
18
|
**NEVER use plan mode (EnterPlanMode).**
|
|
@@ -29,7 +32,7 @@ COMPLEXITY_BAND=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
|
29
32
|
COMPLEXITY_BAND="${COMPLEXITY_BAND:-STANDARD}"
|
|
30
33
|
```
|
|
31
34
|
|
|
32
|
-
**If COMPLEXITY_BAND: TRIVIAL** → skip all phases (A0 through A5), **post the minimal skip marker comment described in "Skip Marker" under Skip Conditions below** (reason: `TRIVIAL complexity band`), then
|
|
35
|
+
**If COMPLEXITY_BAND: TRIVIAL or INVESTIGATION** → skip all phases (A0 through A5), **post the minimal skip marker comment described in "Skip Marker" under Skip Conditions below** (reason: `TRIVIAL complexity band` or `INVESTIGATION complexity band`), then print `ARCHITECT_RESULT:` with `status: SKIPPED` immediately. Do not read any files. This is not an error — trivial single-file changes have no cross-path consistency risk. The skip marker (ending in `<!-- FORGE:ARCHITECT:COMPLETE -->`) is what makes a legitimate skip observable to the headless engine's marker-only gate, so the issue advances to build instead of being stranded at `needs-human`. <!-- Added: forge#679; skip-marker on skip: forge#2689 -->
|
|
33
36
|
|
|
34
37
|
**If COMPLEXITY_BAND: STANDARD or COMPLEX** → proceed to Phase A0 below.
|
|
35
38
|
|
|
@@ -50,14 +53,16 @@ Parse from $ARGUMENTS:
|
|
|
50
53
|
- `--repo {GH_REPO}` — GitHub repo (e.g. `{owner}/{repo}` — resolved from `forge.yaml → project`)
|
|
51
54
|
- `--gh-flag {GH_FLAG}` — gh CLI repo flag (e.g. `-R {owner}/{repo}`)
|
|
52
55
|
- `--repo-path {REPO_PATH}` — local filesystem path to the worktree (e.g. `/path/to/.claude/worktrees/fix/issue-121`); used by Phase A1 grep commands
|
|
53
|
-
- `--files {AFFECTED_FILES}` — space-separated list of files from investigation report (passed by `build
|
|
56
|
+
- `--files "{AFFECTED_FILES}"` — space-separated list of files from investigation report (quoted as one value; passed by `work-on:build` phase B4; empty triggers the empty-list skip)
|
|
57
|
+
|
|
58
|
+
**Fail closed**: if `{NUMBER}`, `--repo`, `--gh-flag` or `--repo-path` is missing, print `ARCHITECT_RESULT:` with `status: BLOCKED`, `comment_url:` empty and `blocker: missing arg <name>`, and stop.
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
You receive no state from the caller. Re-derive everything from GitHub below:
|
|
56
61
|
- `{INVESTIGATION_REPORT}` — full text of FORGE:INVESTIGATOR comment
|
|
57
62
|
- `{CONTEXT_BRIEFING}` — full text of FORGE:CONTEXT comment (empty string if context step was skipped)
|
|
58
63
|
- `{MEMORY_PRIORS}` — structured prior run blocks emitted by investigate Phase 0.5 (empty string if no priors found or investigate predates memory). When non-empty, treat each `[MEMORY PRIOR]` block as a high-confidence prior: if any prior's affected files overlap with the current plan's affected files, explicitly note the prior's root cause and key lesson in the FORGE:ARCHITECT comment's **Prior Run Priors** section (add this section after **Context Briefing** when priors exist). <!-- Added: forge#1316 -->
|
|
59
64
|
|
|
60
|
-
|
|
65
|
+
Read them from GitHub:
|
|
61
66
|
|
|
62
67
|
```bash
|
|
63
68
|
# Read investigation report
|
|
@@ -80,7 +85,7 @@ gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
|
80
85
|
--jq '.[] | select(.body | contains("FORGE:ARCHITECT")) | .body'
|
|
81
86
|
```
|
|
82
87
|
|
|
83
|
-
- If `<!-- FORGE:ARCHITECT -->` comment exists AND `<!-- FORGE:ARCHITECT:COMPLETE -->` is present in the SAME comment → plan already complete
|
|
88
|
+
- If `<!-- FORGE:ARCHITECT -->` comment exists AND `<!-- FORGE:ARCHITECT:COMPLETE -->` is present in the SAME comment → plan already complete: print `ARCHITECT_RESULT:` with `status: COMPLETE` and the existing comment's `comment_url`, EXIT.
|
|
84
89
|
- If `<!-- FORGE:ARCHITECT -->` comment exists BUT `<!-- FORGE:ARCHITECT:COMPLETE -->` is ABSENT → plan was interrupted, delete the partial comment and restart:
|
|
85
90
|
```bash
|
|
86
91
|
COMMENT_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
@@ -435,6 +440,15 @@ Read the 3–5 most relevant files identified. Do NOT read more than 8 files tot
|
|
|
435
440
|
|
|
436
441
|
---
|
|
437
442
|
|
|
443
|
+
## Phase A1.6: Route-Tree Classification for Shared Components *(conditional — skip if no files under `components/` are affected, except `components/ui/primitives/`)* <!-- Added: forge#381 -->
|
|
444
|
+
|
|
445
|
+
When a change adds a new hook call or context dependency to a shared component, classify ALL call sites found in A1 by route context:
|
|
446
|
+
1. **Authenticated routes**: Callers under `app/dashboard/`, `app/(authenticated)/`, or any layout that wraps children with an auth provider (e.g., `UserProvider`, `SessionProvider`).
|
|
447
|
+
2. **Public routes**: Callers under `app/(public)/`, `app/playground/`, `app/(marketing)/`, or any layout without the relevant provider.
|
|
448
|
+
3. **If both categories have callers AND the new hook throws when its provider is absent**: add an explicit implementation step to either (a) guard the hook call with a null-context check, (b) make the hook return a safe default when called outside its provider, or (c) remove the hook from the shared component and move it to the authenticated-route-only caller. Document the split in the FORGE:ARCHITECT affected paths table. Do NOT leave a shared component that crashes public routes to be discovered by the FE review agent.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
438
452
|
## Phase A2: Trace the Data Flow
|
|
439
453
|
|
|
440
454
|
From each entry point identified in A1, trace what happens to the data being changed:
|
|
@@ -449,10 +463,40 @@ For each step in the flow, check:
|
|
|
449
463
|
- Is there a validation or coercion that must be updated consistently?
|
|
450
464
|
- Is there a test that covers this path?
|
|
451
465
|
|
|
466
|
+
**For every field or key read by the changed code, enumerate ALL write paths first** — search across all services for assignments to that field. If multiple code paths write different types to the same field (e.g. dict in the standard path, string in the auth-gated path), the implementation must handle all variants. Do not assume the type you see on the primary code path is the only possible type.
|
|
467
|
+
|
|
452
468
|
Produce a table of ALL files/functions that must change to maintain consistency.
|
|
453
469
|
|
|
454
470
|
---
|
|
455
471
|
|
|
472
|
+
## Phase A2.1: Runtime UID × Volume Ownership Check *(conditional — skip if no Dockerfile or entrypoint is affected)* <!-- Added: forge#323 -->
|
|
473
|
+
|
|
474
|
+
When the PR changes the container's runtime user (Dockerfile `USER` directive, `su-exec`, `gosu`, `setuid`), the architect MUST trace the full write-path chain before writing the implementation plan:
|
|
475
|
+
1. **Enumerate volume mounts**: Read all `docker-compose*.yml` files for the affected service. List every named volume and its container mount point (e.g. `storage_shared:/app/storage`). Docker named volumes are created as root-owned by default — any UID change without a corresponding ownership fix will silently break writes.
|
|
476
|
+
2. **Grep for filesystem writes**: Search the affected service's codebase for all filesystem write operations (`mkdir`, `Path.mkdir`, `write_bytes`, `open(`, `os.makedirs`, `shutil.copy`, `shutil.move`). For each write operation, identify the target path.
|
|
477
|
+
3. **Cross-reference**: For each write path that falls under a named volume mount point, add an explicit implementation step to ensure ownership compatibility before the privilege drop — typically `chown -R <user>:<group> <mount_point>` in the entrypoint script before the `exec su-exec` / `exec gosu` call.
|
|
478
|
+
4. **Add to FORGE:ARCHITECT deliverables table**: List the entrypoint or docker-compose change as an explicit deliverable. Do NOT leave volume ownership to be discovered by the builder or reviewer.
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Phase A2.2: Gate-Condition Caller Sweep *(conditional — when the fix changes a gate condition that guards a function call or restricts a field)* <!-- Added: forge#383 -->
|
|
483
|
+
|
|
484
|
+
Before finalizing the affected-paths table, grep for all callers of the gated function across sibling files in the same service directory. For each caller, verify that the gate condition applied at the call site is semantically correct.
|
|
485
|
+
|
|
486
|
+
```bash
|
|
487
|
+
# Identify the gated function from the issue/contract
|
|
488
|
+
GATED_FUNCTION="{function_being_called_inside_the_gate}"
|
|
489
|
+
SERVICE_DIR=$(dirname {PRIMARY_AFFECTED_FILE})
|
|
490
|
+
# Find all call sites
|
|
491
|
+
grep -rn "$GATED_FUNCTION" "$SERVICE_DIR" --include="*.py" | grep -v "#"
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
For each call site found: read the surrounding gate condition (±10 lines). If the condition includes fields that do NOT require the gated resource (e.g., `extraction_schema` gated behind an LLM key check when `extraction_schema` is processed without an LLM), add that caller file to the FORGE:ARCHITECT affected-paths table with a note explaining the incorrect gate condition.
|
|
495
|
+
|
|
496
|
+
**Do NOT** omit sibling callers from the affected-paths table simply because they were not listed in the issue spec. The architect's scope is determined by code correctness, not by the issue spec's file list. A gate-condition bug that exists identically in 3 router files must be fixed in all 3 — even if the issue only named 1.
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
456
500
|
## Phase A2.5: Pipeline Phase-Dependency Check
|
|
457
501
|
|
|
458
502
|
**Skip if**: None of the `{AFFECTED_FILES}` are Forge pipeline command files (i.e., no file matches `commands/**/*.md`). When skipped, proceed directly to A3.
|
|
@@ -471,7 +515,7 @@ For each affected `commands/*.md` file, identify what it produces that other pha
|
|
|
471
515
|
|
|
472
516
|
1. **Comment markers** — HTML comment markers the phase writes to GitHub (e.g., `<!-- FORGE:ARCHITECT -->`, `<!-- FORGE:ARCHITECT:COMPLETE -->`). Search for `<!-- FORGE:` patterns in the affected file.
|
|
473
517
|
2. **Structured output blocks** — named result blocks returned to the caller (e.g., `BUILD_RESULT:`, `VALIDATE_RESULT:`, `IMPLEMENT_RESULT:`). Search for lines ending in `:` that start a return block.
|
|
474
|
-
3. **Skill() invocation signatures** — parameters the file passes when invoking subcommands (e.g., `Skill("work-on:build:architect", args="... --files ...")`). Search for `Skill(` calls.
|
|
518
|
+
3. **Skill() invocation signatures** — parameters the file passes when invoking subcommands (e.g., `Skill("{FORGE_SKILL_PREFIX}work-on:build:architect", args="... --files ...")`). Search for `Skill(` calls.
|
|
475
519
|
4. **Label state machine transitions** — `gh issue edit ... --add-label ... --remove-label ...` calls that define which label is set at the end of this phase.
|
|
476
520
|
|
|
477
521
|
**P2: Find downstream phases that consume these artefacts**
|
|
@@ -668,12 +712,13 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:ARCHITECT -->
|
|
|
668
712
|
|
|
669
713
|
## Skip Conditions
|
|
670
714
|
|
|
671
|
-
Skip the planning work (Phases A0–A5) and
|
|
715
|
+
Skip the planning work (Phases A0–A5) and print `ARCHITECT_RESULT:` with `status: SKIPPED` — **but always post the Skip Marker below first** — if:
|
|
672
716
|
- **COMPLEXITY_BAND: TRIVIAL** — checked via FORGE:FAST_PATH comment at entry (see guard above) <!-- Primary skip path: forge#679 -->
|
|
673
717
|
- Issue creates only **new files** with no callers to find (e.g. a new command file with no existing integration point yet)
|
|
674
718
|
- Issue is a 1-file config or docs edit with no code logic
|
|
675
719
|
- Issue title starts with "docs:" or "chore:"
|
|
676
720
|
- `{AFFECTED_FILES}` is empty
|
|
721
|
+
- **COMPLEXITY_BAND: INVESTIGATION** — investigation tasks produce issues, not code
|
|
677
722
|
|
|
678
723
|
When skipped, the builder proceeds with investigation report + context briefing only (see Integration Point below — the builder falls back gracefully on an empty plan).
|
|
679
724
|
|
|
@@ -681,7 +726,7 @@ When skipped, the builder proceeds with investigation report + context briefing
|
|
|
681
726
|
|
|
682
727
|
**Do not post nothing.** The headless engine gate (`bin/engine/phases.mjs` → `architect.detectOutcome`) treats a **missing** `FORGE:ARCHITECT:COMPLETE` sentinel as a phase **failure**, retries to `maxAttempts` (3), then strands the issue at `needs-human`. It reads GitHub comment markers only — it cannot see this subcommand's in-process "empty plan" return value. So a skip that posts no marker is indistinguishable from a crashed architect run and deterministically strands **every** `chore:`/`docs:`/trivial issue.
|
|
683
728
|
|
|
684
|
-
On **every** skip branch above, post exactly this minimal comment (substitute the concrete `{SKIP_REASON}`, e.g. `chore:/docs: title`, `TRIVIAL complexity band`, `new files only — no callers`, or `AFFECTED_FILES empty`) **before**
|
|
729
|
+
On **every** skip branch above, post exactly this minimal comment (substitute the concrete `{SKIP_REASON}`, e.g. `chore:/docs: title`, `TRIVIAL complexity band`, `new files only — no callers`, or `AFFECTED_FILES empty`) **before** printing `ARCHITECT_RESULT:`:
|
|
685
730
|
|
|
686
731
|
```bash
|
|
687
732
|
# The skip marker is mandatory and un-gated by design — posting it IS the point
|
|
@@ -704,22 +749,39 @@ The empty-plan-with-marker changes no downstream behavior — the builder alread
|
|
|
704
749
|
|
|
705
750
|
---
|
|
706
751
|
|
|
707
|
-
##
|
|
752
|
+
## Result Block (MANDATORY on every path)
|
|
753
|
+
|
|
754
|
+
Your final reply MUST end with exactly one block, printed after the plan, partial, or skip-marker comment has been posted. `work-on:build` reads only this block.
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
ARCHITECT_RESULT:
|
|
758
|
+
status: COMPLETE | PARTIAL | SKIPPED | BLOCKED
|
|
759
|
+
comment_url: {URL of the FORGE:ARCHITECT comment, or empty}
|
|
760
|
+
blocker: {description if status=BLOCKED, else empty}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
- `COMPLETE` — plan posted with `FORGE:ARCHITECT:COMPLETE` (also the result when the Resume Check finds a complete plan).
|
|
764
|
+
- `PARTIAL` — budget exceeded; `FORGE:ARCHITECT:PARTIAL` posted.
|
|
765
|
+
- `SKIPPED` — a skip condition applied and the Skip Marker was posted (give its URL).
|
|
766
|
+
- `BLOCKED` — conflicting constraints that cannot be resolved, or a missing required arg. Post a comment describing the conflict and add `needs-human` (guard with `DRY_RUN`) before printing the block.
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
770
|
+
## Integration Point
|
|
708
771
|
|
|
709
|
-
This module runs
|
|
772
|
+
This module runs as build phase B4 — after Context Gathering, before Implement:
|
|
710
773
|
|
|
711
774
|
```
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
3C.6 → [THIS MODULE] Architecture Planning (FORGE:ARCHITECT comment)
|
|
775
|
+
B3 → Context Gathering (FORGE:CONTEXT comment)
|
|
776
|
+
B4 → [THIS MODULE] Architecture Planning (FORGE:ARCHITECT comment)
|
|
715
777
|
Phase A0: Read Custom Instructions and Project Conventions (highest precedence)
|
|
716
|
-
Phase A1: Read Entry Points
|
|
717
|
-
Phase A2: Trace the Data Flow
|
|
778
|
+
Phase A1: Read Entry Points (A1.5 Prior Decision Injection, A1.6 Route-Tree Classification)
|
|
779
|
+
Phase A2: Trace the Data Flow (A2.1 Runtime UID x Volume Ownership, A2.2 Gate-Condition Caller Sweep)
|
|
718
780
|
Phase A2.5: Pipeline Phase-Dependency Check
|
|
719
781
|
Phase A3: Consistency Rules
|
|
720
782
|
Phase A4: Sequence the Implementation
|
|
721
783
|
Phase A5: Risk Assessment
|
|
722
|
-
|
|
784
|
+
B5 → Implement (builder reads plan, implements ALL affected paths)
|
|
723
785
|
```
|
|
724
786
|
|
|
725
787
|
The builder agent reads the `<!-- FORGE:ARCHITECT -->` comment as its **primary input** before writing any code. The raw issue body is secondary context. If the architect step was skipped, the builder falls back to investigation report + contract.
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Pre-implementation context gathering — surfaces historical findings, bug patterns, and related code paths before the builder writes any code
|
|
3
|
-
argument-hint: "
|
|
3
|
+
argument-hint: "{NUMBER} --repo {GH_REPO} --gh-flag {GH_FLAG} --repo-path {WORKTREE} --files \"<space-separated>\" [--functions \"<names>\"]"
|
|
4
|
+
context: fork
|
|
4
5
|
---
|
|
5
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
7
8
|
|
|
8
9
|
# work-on/build/context — Pre-Implementation Context Gathering
|
|
9
10
|
|
|
10
|
-
**Invoked by**: `work-on
|
|
11
|
+
**Invoked by**: `work-on:build` (phase B3), between Builder Contract and Architect, via `Skill()`. Runs in its own forked context: you see only this spec and your args — re-read all other state from GitHub/git.
|
|
11
12
|
**Time budget**: Max 2 minutes of queries. Skip any query that times out.
|
|
12
|
-
**Output**: Post `<!-- FORGE:CONTEXT -->` comment on the issue, then
|
|
13
|
+
**Output**: Post `<!-- FORGE:CONTEXT -->` comment on the issue, then print exactly one `CONTEXT_RESULT:` block (see Result Block) as your final reply. Every exit path — success, partial, skip, failure — prints it.
|
|
13
14
|
|
|
14
15
|
<!-- FORGE:SPEC_LOADED — work-on/build/context.md loaded and active. Agent is bound by this spec. -->
|
|
15
16
|
|
|
@@ -26,7 +27,7 @@ COMPLEXITY_BAND=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
|
26
27
|
COMPLEXITY_BAND="${COMPLEXITY_BAND:-STANDARD}"
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
**If COMPLEXITY_BAND: TRIVIAL** → skip all phases (C-1 through C4), post NO comment,
|
|
30
|
+
**If COMPLEXITY_BAND: TRIVIAL or INVESTIGATION** → skip all phases (C-1 through C4), post NO comment, print `CONTEXT_RESULT:` with `status: SKIPPED` and `comment_url:` empty immediately. Do not query GitHub, do not read files. This is not an error — trivial single-file changes (and investigation tasks, which produce issues rather than code) have no institutional memory to surface. <!-- Added: forge#679 -->
|
|
30
31
|
|
|
31
32
|
**If COMPLEXITY_BAND: STANDARD or COMPLEX** → proceed to Phase C-1 below.
|
|
32
33
|
|
|
@@ -47,8 +48,10 @@ Parse from `$ARGUMENTS`:
|
|
|
47
48
|
- `--repo {GH_REPO}` — GitHub repo (e.g. `{owner}/{repo}` — resolved from `forge.yaml → project`)
|
|
48
49
|
- `--gh-flag {GH_FLAG}` — gh CLI repo flag (e.g. `-R {owner}/{repo}`)
|
|
49
50
|
- `--repo-path {REPO_PATH}` — local filesystem path to the worktree (e.g. `/path/to/.claude/worktrees/fix/issue-121`); used by Phase C3 grep commands
|
|
50
|
-
-
|
|
51
|
-
- `--functions {FUNCTION_NAMES}` — space-separated function/class names extracted from the Builder Contract deliverables table (optional)
|
|
51
|
+
- `--files "{AFFECTED_FILES}"` — space-separated file paths (quoted as one value; an empty value is allowed and triggers the empty-list skip)
|
|
52
|
+
- `--functions "{FUNCTION_NAMES}"` — space-separated function/class names extracted from the Builder Contract deliverables table (optional)
|
|
53
|
+
|
|
54
|
+
**Fail closed**: if `{NUMBER}`, `--repo`, `--gh-flag` or `--repo-path` is missing, print `CONTEXT_RESULT:` with `status: SKIPPED` and `comment_url:` empty plus a one-line `note: missing arg <name>` and stop (context is advisory — a missing arg must never block the build, but it must be visible).
|
|
52
55
|
|
|
53
56
|
**Graceful skip for empty FUNCTION_NAMES**: If `--functions` is absent or `{FUNCTION_NAMES}` is empty, Phase C3 produces zero for-loop iterations and is effectively skipped — no error, no output for that phase. This is expected behavior when the contract does not name specific functions.
|
|
54
57
|
|
|
@@ -674,6 +677,12 @@ If `GIST_SUMMARIES` is non-empty, it will be included in the `### Prior Investig
|
|
|
674
677
|
|
|
675
678
|
---
|
|
676
679
|
|
|
680
|
+
## Batch Execution of C1–C4 (MANDATORY) <!-- Added: forge#1826 -->
|
|
681
|
+
|
|
682
|
+
C1, C2 (including the direct commit-body read and the pickaxe pass), C3, and C4 below are mutually independent — none consumes another's output. Issue ALL of their underlying `gh`/`git`/`grep` calls as a single batch of parallel tool calls in one message, not as four sequential steps. This is the same rule as the "independent tool calls → same message" convention used elsewhere in the pipeline; it turns a ~4x serial round-trip chain (each `gh api` call is 0.5-2s) into one wall-clock round-trip. The per-file loop inside C1 and the per-function loop inside C3 are themselves independent across iterations — include every iteration's call in the same batch rather than looping turn-by-turn. Total budget is still 20s per query / 2 min overall; batching only removes serialization, it does not change the timeout.
|
|
683
|
+
|
|
684
|
+
---
|
|
685
|
+
|
|
677
686
|
## Phase C1: Past Review Findings on These Files
|
|
678
687
|
|
|
679
688
|
**Primary path — Forge Ledger** (O(1) local index lookup, zero API calls): <!-- Added: forge#1732 -->
|
|
@@ -922,36 +931,70 @@ gh issue comment {NUMBER} -R {GH_REPO} --body "<!-- FORGE:CONTEXT -->
|
|
|
922
931
|
- Each `gh issue list` call: timeout after 20s, skip if exceeded
|
|
923
932
|
- Each `gh pr list` call: timeout after 20s, skip if exceeded
|
|
924
933
|
- Each `grep -r` call: timeout after 10s, skip if exceeded
|
|
925
|
-
- Total wall time budget: **2 minutes** (C-1 through C4 combined). If budget exceeded, post partial results with `<!-- FORGE:CONTEXT:PARTIAL -->` marker instead of `COMPLETE
|
|
934
|
+
- Total wall time budget: **2 minutes** (C-1 through C4 combined). If budget exceeded, post partial results with `<!-- FORGE:CONTEXT:PARTIAL -->` marker instead of `COMPLETE`, and report `status: PARTIAL` in `CONTEXT_RESULT:`.
|
|
935
|
+
- C1–C4 are issued as one parallel batch (see Batch Execution above) — the 2-minute budget is wall time for the batch, not per step.
|
|
926
936
|
|
|
927
937
|
---
|
|
928
938
|
|
|
929
939
|
## Skip Conditions
|
|
930
940
|
|
|
931
|
-
Skip
|
|
932
|
-
- **COMPLEXITY_BAND: TRIVIAL** — checked via FORGE:FAST_PATH comment at entry (see guard above) <!-- Primary skip path: forge#679 -->
|
|
941
|
+
Skip the context gathering (print `CONTEXT_RESULT:` with `status: SKIPPED`) if:
|
|
942
|
+
- **COMPLEXITY_BAND: TRIVIAL or INVESTIGATION** — checked via FORGE:FAST_PATH comment at entry (see guard above); post NO comment <!-- Primary skip path: forge#679 -->
|
|
933
943
|
- Issue is a 1-file config or docs edit with no code logic
|
|
934
944
|
- The affected files have zero git history (new files being created)
|
|
935
945
|
- `{AFFECTED_FILES}` is empty (investigation produced no file list)
|
|
936
946
|
|
|
947
|
+
**Minimal marker on every non-TRIVIAL skip (forge#3123)**: for STANDARD/COMPLEX the phase-trail gate (`scripts/verify-phase-trail.sh`) requires a `FORGE:CONTEXT` marker, so a skip must still post an empty marker comment (idempotent: skip if a `FORGE:CONTEXT` comment already exists), otherwise a re-run can never satisfy the gate:
|
|
948
|
+
|
|
949
|
+
```bash
|
|
950
|
+
EXISTING_CONTEXT=$(gh api "repos/{GH_REPO}/issues/{NUMBER}/comments" --paginate \
|
|
951
|
+
--jq '[.[] | select(.body | contains("<!-- FORGE:CONTEXT -->"))] | length' \
|
|
952
|
+
| awk '{s+=$1} END {print s+0}')
|
|
953
|
+
if [ "${DRY_RUN:-false}" = "true" ]; then
|
|
954
|
+
echo "[DRY_RUN] would post minimal FORGE:CONTEXT marker on #{NUMBER}"
|
|
955
|
+
elif [ "$EXISTING_CONTEXT" -eq 0 ]; then
|
|
956
|
+
gh issue comment "{NUMBER}" -R "{GH_REPO}" --body "<!-- FORGE:CONTEXT -->
|
|
957
|
+
## Implementation Context for #{NUMBER}
|
|
958
|
+
|
|
959
|
+
Context gathering skipped: {REASON — config/docs-only edit | new files, no git history | empty affected-file list}. No institutional memory to surface.
|
|
960
|
+
|
|
961
|
+
<!-- FORGE:CONTEXT:COMPLETE -->"
|
|
962
|
+
fi
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
---
|
|
966
|
+
|
|
967
|
+
## Result Block (MANDATORY on every path)
|
|
968
|
+
|
|
969
|
+
Your final reply MUST end with exactly one block, printed after any marker comment has been posted. `work-on:build` reads only this block.
|
|
970
|
+
|
|
971
|
+
```
|
|
972
|
+
CONTEXT_RESULT:
|
|
973
|
+
status: COMPLETE | PARTIAL | SKIPPED
|
|
974
|
+
comment_url: {URL of the FORGE:CONTEXT comment, or empty when no comment was posted}
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
- `COMPLETE` — full `FORGE:CONTEXT:COMPLETE` comment posted.
|
|
978
|
+
- `PARTIAL` — budget exceeded or a source errored; `FORGE:CONTEXT:PARTIAL` posted.
|
|
979
|
+
- `SKIPPED` — any skip condition (TRIVIAL/INVESTIGATION band: no comment; other skips: minimal marker comment, give its URL if you posted or found one).
|
|
980
|
+
- An error, timeout or missing arg is never a reason to omit the block and never blocks the build.
|
|
981
|
+
|
|
937
982
|
---
|
|
938
983
|
|
|
939
|
-
## Integration Point
|
|
984
|
+
## Integration Point
|
|
940
985
|
|
|
941
|
-
This module runs
|
|
986
|
+
This module runs as build phase B3 — after the Builder Contract is posted, before Architect:
|
|
942
987
|
|
|
943
988
|
```
|
|
944
|
-
|
|
945
|
-
|
|
989
|
+
B2 → Builder Contract posted
|
|
990
|
+
B3 → [THIS MODULE] Context gathering (max 2 min)
|
|
946
991
|
Phase C-1: Authoritative Devdocs (project-resident knowledge — highest precedence)
|
|
947
992
|
Phase C-0.5: Active Peer Claims Reader (conditional — orchestration only)
|
|
948
993
|
Phase C0.5: Danger-Zone Rule Cards (fixed 400-token slot — forge#1744)
|
|
949
994
|
Phase C0: Prior Investigation Findings (from Gists)
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
Phase C4: Successful Similar Implementations
|
|
954
|
-
3F → Implement (builder now has context briefing)
|
|
995
|
+
Phases C1–C4: batched in parallel (Past Review Findings, Past Bugs, Related Code Paths, Similar Implementations)
|
|
996
|
+
B4 → Architect
|
|
997
|
+
B5 → Implement (builder now has context briefing)
|
|
955
998
|
```
|
|
956
999
|
|
|
957
1000
|
The builder agent reads the `<!-- FORGE:CONTEXT -->` comment before writing any code. If the context step was skipped, the builder proceeds with investigation report + contract only.
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Implementation agent — writes code, makes commits, posts builder comment
|
|
3
|
-
argument-hint: "
|
|
3
|
+
argument-hint: "{NUMBER} --repo {GH_REPO} --gh-flag \"{GH_FLAG}\" --worktree {WORKTREE} --branch {BRANCH} --base {PR_BASE} [--fix-acceptance \"<failed checks>\"]"
|
|
4
|
+
context: fork
|
|
4
5
|
---
|
|
5
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
7
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
7
8
|
|
|
8
9
|
# work-on/build/implement — Implementation Subcommand
|
|
9
10
|
|
|
11
|
+
> **Skill Name Resolution (forked phase)**: `{FORGE_SKILL_PREFIX}` is the namespace this skill itself was invoked under — invoked as `forgedock:work-on:…` → `forgedock:` (nesting `:`); as `work-on:…` → empty (`install.sh`); as `forge-work-on-…` → `forge-` (Codex, nesting `-`); OpenCode → empty with `-` nesting. Confirm the target name in the available-skills list before calling it. A forked phase receives no resolved value from its caller; never guess, and if the target skill is not listed return BLOCKED "skill not found: <name>".
|
|
12
|
+
|
|
10
13
|
**Input**: $ARGUMENTS
|
|
11
14
|
|
|
12
|
-
**Invoked by**: `work-on
|
|
13
|
-
**Output**: Write code,
|
|
15
|
+
**Invoked by**: `work-on:build`, after the worktree is created and context is gathered. This skill runs in an isolated forked context: it sees only this text and its args, so it re-reads all other state from GitHub/git.
|
|
16
|
+
**Output**: Write code, stage changes, post `<!-- FORGE:BUILDER -->` comment, and end with exactly one `IMPLEMENT_RESULT:` block as the final reply.
|
|
17
|
+
|
|
18
|
+
**Result-on-every-exit rule**: every exit path — success, `ALREADY_DONE`, `INVESTIGATION_COMPLETE`, every guard, every failure — MUST print the `IMPLEMENT_RESULT:` block (see Output) as the final reply. Never exit with a bare `exit 1` or free text. Failures use `status: BLOCKED` with a `blocker` line.
|
|
14
19
|
|
|
15
20
|
**Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
|
|
16
21
|
**NEVER use plan mode (EnterPlanMode).**
|
|
@@ -25,11 +30,18 @@ Parse from $ARGUMENTS:
|
|
|
25
30
|
- `{NUMBER}` — issue number (required)
|
|
26
31
|
- `--repo {GH_REPO}` — GitHub repo (e.g. `{owner}/{repo}` — resolved from `forge.yaml → project`)
|
|
27
32
|
- `--gh-flag {GH_FLAG}` — gh CLI repo flag (e.g. `-R {owner}/{repo}`)
|
|
28
|
-
- `--worktree {WORKTREE_PATH}` — absolute path to the git worktree (
|
|
29
|
-
- `--branch {BRANCH}` — feature branch name (e.g. `feat/my-feature`)
|
|
33
|
+
- `--worktree {WORKTREE_PATH}` — absolute path to the git worktree (required)
|
|
34
|
+
- `--branch {BRANCH}` — feature branch name (e.g. `feat/my-feature`) (required)
|
|
35
|
+
- `--base {PR_BASE}` — PR base branch name without the `origin/` prefix (required; computed by the router and passed down; used by the cross-lane import guard and migration collision check)
|
|
36
|
+
|
|
37
|
+
**Fail closed**: if `{NUMBER}`, `--repo`, `--worktree`, `--branch`, or `--base` is missing or empty, or `{WORKTREE_PATH}` is not an existing directory, print `IMPLEMENT_RESULT:` with `status: BLOCKED` and `blocker: missing or invalid arg: <name>` and STOP. `{GH_FLAG}` defaults to `-R {GH_REPO}` when absent.
|
|
38
|
+
|
|
39
|
+
**Derived values**: `{CHANGED_FILES}` (used below) is the space-separated list of files you changed in the worktree — derive it with `git -C {WORKTREE_PATH} diff --name-only HEAD` plus any untracked files from `git -C {WORKTREE_PATH} ls-files --others --exclude-standard`. `{GH_REPO}` and `{GH_FLAG}` as parsed above.
|
|
30
40
|
|
|
31
41
|
---
|
|
32
42
|
|
|
43
|
+
- `--fix-acceptance "<failed checks>"` — optional. Set only by build B6.5 after a failed acceptance gate. In this mode, skip I1–I2 planning: read the listed failing `ACCEPTANCE_CHECK` ids from the FORGE:INVESTIGATOR comment, change the code in `{WORKTREE_PATH}` until each check's target/matcher holds (do not edit the checks), update the BUILDER comment's `### Changes` list, and return `IMPLEMENT_RESULT: status: COMPLETE`. If a check is impossible as written (contradicts the contract), return `status: BLOCKED` with that reason.
|
|
44
|
+
|
|
33
45
|
## Phase I1: Load Context from GitHub
|
|
34
46
|
|
|
35
47
|
Read the full context chain before writing a single line of code:
|
|
@@ -56,7 +68,7 @@ gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
|
56
68
|
```
|
|
57
69
|
|
|
58
70
|
**Resume check**:
|
|
59
|
-
- If `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment → implementation is done — EXIT and
|
|
71
|
+
- If `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment → implementation is done — EXIT and print `IMPLEMENT_RESULT: status: ALREADY_DONE` as the final reply.
|
|
60
72
|
- If `<!-- FORGE:BUILDER -->` exists BUT `<!-- FORGE:BUILDER:COMPLETE -->` is ABSENT → implementation was interrupted after the comment was posted but before the commit (validate.md V5). Delete the partial comment and restart from Phase I2:
|
|
61
73
|
```bash
|
|
62
74
|
PARTIAL_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
|
|
@@ -102,7 +114,7 @@ Extract from contract:
|
|
|
102
114
|
| Refactor / Maintenance | Implement directly following contract deliverables |
|
|
103
115
|
| Investigation | Spawn research agents, create GitHub issues for findings, skip to I5 |
|
|
104
116
|
|
|
105
|
-
**Investigation task special case**: Research deeply, create GitHub issues for each finding using the Pipeline Issue Template (see `commands/issue.md` § "Pipeline Issue Template"). Each issue MUST include `## Problem`, `## Affected Files`, and `## Acceptance Criteria`. Create each issue via the `/issue` create-hook's programmatic invocation contract (see `commands/issue.md` § "Programmatic Invocation Contract") — `Skill(skill="issue", args="--title \"...\" --body-file <path> --label ...")` — instead of calling the raw issue-creation command directly; this gets dedup and body validation for free. Post a deliverables comment listing the created issues, close the original issue,
|
|
117
|
+
**Investigation task special case**: Research deeply, create GitHub issues for each finding using the Pipeline Issue Template (see `commands/issue.md` § "Pipeline Issue Template"). Each issue MUST include `## Problem`, `## Affected Files`, and `## Acceptance Criteria`. Create each issue via the `/issue` create-hook's programmatic invocation contract (see `commands/issue.md` § "Programmatic Invocation Contract") — `Skill(skill="{FORGE_SKILL_PREFIX}issue", args="--title \"...\" --body-file <path> --label ...")` — instead of calling the raw issue-creation command directly; this gets dedup and body validation for free. Post a deliverables comment listing the created issues, close the original issue, and print `IMPLEMENT_RESULT: status: INVESTIGATION_COMPLETE` as the final reply (the build skill passes this up and the router skips review). <!-- Added: forge#2090 -->
|
|
106
118
|
|
|
107
119
|
---
|
|
108
120
|
|
|
@@ -118,8 +130,12 @@ Work in `{WORKTREE_PATH}`. Follow the contract deliverables table exactly — im
|
|
|
118
130
|
- **Library callback verification**: When writing a lambda or callable that will be passed to a library/framework parameter (e.g., `prepared_statement_name_func=lambda: ""`, `key=lambda x: ...`), you MUST verify the expected calling convention BEFORE writing it. Check the library's default value for that parameter, its documentation, or its source code. A lambda with wrong arity causes `TypeError` at runtime — this is invisible to static analysis and linting. The P0 incident from PR #14391 was caused by `lambda _: ""` (1 arg) passed where SQLAlchemy expects 0 args.
|
|
119
131
|
- **Worktree-aware path derivation**: When writing shell code that derives repository paths, ALWAYS use `git rev-parse --show-toplevel` for the repo root in regular checkouts, or `git rev-parse --git-common-dir` (then `dirname`) to get the shared `.git` directory when the context may be a linked worktree. For worktree cleanup code specifically, ALWAYS use `--git-common-dir` — `--show-toplevel` returns the worktree path itself, NOT the main repo root. NEVER use `pwd`, relative paths, or `dirname` chains on `--show-toplevel` output for repo root derivation. Test path logic for both regular checkouts and linked worktrees. (Ref: review-findings #104, #105 — 4 PATH_DERIVATION defects, 15% of all review findings)
|
|
120
132
|
- **State machine completeness verification**: When implementing routing logic, state transition tables, or phase dispatch code (e.g., adding a `Skill()` call in a routing loop, adding a new phase to a state machine, creating a new subcommand file), you MUST run three checks BEFORE staging: (1) **Routing target existence** — for each `Skill("subcommand", ...)` call or file reference in routing logic, verify the target file exists at `commands/{subcommand-path}.md`; (2) **State reachability** — for each declared state or phase, verify at least one prior state or entry condition in the router routes to it; (3) **Subcommand invocation wiring** — for each `commands/work-on/*.md` file that declares its invocation condition (e.g., `Invoked by work-on.md routing loop, when X`), verify that condition is actually handled in the router. A missing target file, an unreachable state, or a declared-but-unwired subcommand will not surface until review. (Ref: review-findings #85, #116, #137 — 4 ROUTING defects, 15% of all review findings)
|
|
121
|
-
- **Migration safety checklist** *(trigger: diff includes `*.sql` files or files under a `migrations/` path)*: When any migration file is in the diff, verify ALL of the following BEFORE staging. Fix each violation inline — do not defer to review or the quality gate: (a) **Rollback file**: a corresponding down/rollback migration file exists (e.g. `0042_down_*.sql`, `rollback_*.sql`) or the migration is explicitly self-reversing (DROP of a previously-added column); (b) **NOT NULL safety**: any `ADD COLUMN ... NOT NULL` either includes a `DEFAULT` clause or is preceded by a backfill step — a NOT NULL column without DEFAULT locks the table and fails on existing rows; (c) **Constraint name consistency**: constraint names in the migration match ORM model declarations — a mismatch causes `alembic stamp` and FK introspection to silently diverge; (d) **CREATE TRIGGER idempotency**: every `CREATE TRIGGER` uses `CREATE OR REPLACE TRIGGER` or is preceded by `DROP TRIGGER IF EXISTS` — a bare `CREATE TRIGGER` fails on re-run in test and CI environments; (e) **Migration prefix uniqueness**: confirm the new file's numeric prefix does not already exist in the `infra/migrations/` tree (`ls infra/migrations/*.sql | grep -
|
|
122
|
-
-
|
|
133
|
+
- **Migration safety checklist** *(trigger: diff includes `*.sql` files or files under a `migrations/` path)*: When any migration file is in the diff, verify ALL of the following BEFORE staging. Fix each violation inline — do not defer to review or the quality gate: (a) **Rollback file**: a corresponding down/rollback migration file exists (e.g. `0042_down_*.sql`, `rollback_*.sql`) or the migration is explicitly self-reversing (DROP of a previously-added column); (b) **NOT NULL safety**: any `ADD COLUMN ... NOT NULL` either includes a `DEFAULT` clause or is preceded by a backfill step — a NOT NULL column without DEFAULT locks the table and fails on existing rows; (c) **Constraint name consistency**: constraint names in the migration match ORM model declarations — a mismatch causes `alembic stamp` and FK introspection to silently diverge; (d) **CREATE TRIGGER idempotency**: every `CREATE TRIGGER` uses `CREATE OR REPLACE TRIGGER` or is preceded by `DROP TRIGGER IF EXISTS` — a bare `CREATE TRIGGER` fails on re-run in test and CI environments; (e) **Migration prefix uniqueness**: confirm the new file's numeric prefix does not already exist in the `infra/migrations/` tree (`ls infra/migrations/*.sql | xargs -n1 basename | grep -oE '^[0-9]+' | sort | uniq -d` prints duplicates) — a duplicate prefix hard-fails the deploy gate regardless of file content. <!-- Added: forge#373 -->
|
|
134
|
+
- **Cross-lane import guard**: Before adding any `import` or `from X import Y` statement for a service-internal module (`app.*`), verify the module exists on the PR's base branch (`{PR_BASE}`) — NOT just on your local disk or a milestone branch. Run `git show origin/{PR_BASE}:{module_path}.py` (replacing dots with slashes) to confirm. If the module only exists on a milestone branch, do NOT import it — find an alternative implementation or make the import conditional with a `try/except ImportError` fallback. A milestone-only import on a fast-lane PR will crash production on every request with `ModuleNotFoundError`. <!-- Added: forge#277 -->
|
|
135
|
+
- **Deliverable-type consistency check**: Before committing, compare the actual output against the CONTRACT's deliverable list. If the CONTRACT explicitly states "no code changes required", "docs only", or "configuration update only" AND the diff introduces new executable files (`.py`, `.js`, `.ts`, `.sh` — not test, config, or documentation files), STOP. Do NOT commit. Re-read the CONTRACT, the investigation recommendation, and the ARCHITECT plan. If all three agree that code is needed, update the CONTRACT comment to reflect the new deliverable type before proceeding. If only the builder decided to add code without contract support, discard the code change and implement the contracted deliverable instead. <!-- Added: forge#279 -->
|
|
136
|
+
- **Pipeline check documentation — generalization rule**: When writing or updating pipeline check documentation (in `commands/*.md`), describe the **bug class**, not a specific incident. Do NOT embed: PR numbers, issue numbers, run IDs, timestamps, function names, dollar amounts, or multi-sentence incident timelines in check prose or `**Evidence**:` blocks. One brief HTML comment `<!-- Added: forge#NNN -->` is acceptable per check for traceability. `**Evidence**:` blocks must describe the vulnerability pattern (what the class of bug looks like, why it's dangerous) — not narrate a single historical occurrence. CHANGELOG entries may reference originating issues; command prompt text must not.
|
|
137
|
+
- **Endpoint response contract consumer tracing**: When changing an endpoint's response body shape or status field values (e.g., changing `"status": "healthy"` to `"status": "ok"`, renaming response keys, removing fields), grep the full repo for ALL consumers of that response body before committing. Consumers are not limited to the service being changed — they include deploy scripts, CI health checks, monitoring configs, docker-compose healthcheck definitions, Traefik probes, and any script that parses or pattern-matches on the response body. Run: `grep -rn "{old_value}\|{endpoint_path}" scripts/ infra/ .github/ docker-compose*.yml traefik/ 2>/dev/null`. All consumers whose behavior depends on the old response format MUST be updated in the same PR — a response contract change that updates only one consumer while leaving others on the old format is a deploy-time breakage. <!-- Added: forge#321 -->
|
|
138
|
+
- If you discover the contract is wrong (e.g. a file doesn't exist, a function has a different signature): STOP, post a comment on the issue explaining the discrepancy, add label `needs-human`, and EXIT with `IMPLEMENT_RESULT: status: BLOCKED` and a `blocker` describing the discrepancy
|
|
123
139
|
|
|
124
140
|
**Worktree working directory**:
|
|
125
141
|
```bash
|
|
@@ -143,7 +159,7 @@ Scan changed files for newly introduced env var references:
|
|
|
143
159
|
cd {WORKTREE_PATH}
|
|
144
160
|
# Collect all env var names referenced in changed files
|
|
145
161
|
NEW_ENV_VARS=$(grep -rnE "os\.getenv\(|process\.env\." {CHANGED_FILES} 2>/dev/null \
|
|
146
|
-
| grep -
|
|
162
|
+
| grep -oE "(os\.getenv\(['\"]|process\.env\.)[A-Za-z0-9_]+" \
|
|
147
163
|
| sed "s/os\.getenv\(['\"]//; s/process\.env\.//" \
|
|
148
164
|
| sort -u)
|
|
149
165
|
|
|
@@ -203,6 +219,33 @@ grep -rnE "(api_key|secret|password|token|credential)\s*=\s*(f?['\"]|\`)[^{'\"\`
|
|
|
203
219
|
|
|
204
220
|
**If HARDCODED IP or HARDCODED CREDENTIAL is printed**: replace the literal value with a config reference before proceeding to Phase I4. This is a hard blocker — do not stage hardcoded secrets or IPs.
|
|
205
221
|
|
|
222
|
+
### Check 4 — SDK/API Literal sync advisory
|
|
223
|
+
|
|
224
|
+
**Trigger**: the diff contains `Literal[` in a schema file.
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Detect Literal type changes in API schema files
|
|
228
|
+
cd {WORKTREE_PATH}
|
|
229
|
+
LITERAL_CHANGES=$(git diff HEAD -- | grep -E '^\+.*Literal\[' | grep -v '^\+\+\+')
|
|
230
|
+
if [ -n "$LITERAL_CHANGES" ]; then
|
|
231
|
+
echo "SDK SYNC ADVISORY: Literal type changed in schema."
|
|
232
|
+
echo "Changed Literal lines:"
|
|
233
|
+
echo "$LITERAL_CHANGES"
|
|
234
|
+
echo ""
|
|
235
|
+
echo "ACTION REQUIRED — verify SDK method/type lists match new API schema:"
|
|
236
|
+
echo " - sdk/python/*/client.py: check _valid_methods or equivalent list"
|
|
237
|
+
echo " - sdk/node/src/index.ts: check JSDoc @param Literal type annotation"
|
|
238
|
+
echo " - web/public/openapi*.json: check enum arrays for affected field"
|
|
239
|
+
echo " - web/public/openapi-versions/*.json: check all versioned specs"
|
|
240
|
+
echo ""
|
|
241
|
+
echo "Inconsistency example: API schema narrows Literal['GET','POST','PUT','PATCH','DELETE']"
|
|
242
|
+
echo "to Literal['GET','POST'] but SDK JSDoc still lists all 5 methods — API returns 422"
|
|
243
|
+
echo "for callers following SDK docs. This produces silent user-facing failures."
|
|
244
|
+
fi
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
This advisory is informational — it does NOT block the commit. But the implementer MUST check each listed file and add to the implementation scope if any SDK/spec file still documents the removed/changed Literal values. If SDK files need changes, add them to the current PR rather than leaving the inconsistency for review to catch.
|
|
248
|
+
|
|
206
249
|
---
|
|
207
250
|
|
|
208
251
|
## Phase I4: Stage Changes
|
|
@@ -214,7 +257,7 @@ Migration collision check (if applicable):
|
|
|
214
257
|
git fetch origin
|
|
215
258
|
git log --oneline origin/{PR_BASE}..HEAD -- {MIGRATION_PATHS}
|
|
216
259
|
```
|
|
217
|
-
If a collision is detected, post a comment
|
|
260
|
+
If a collision is detected, post a comment, add `needs-human` label before staging, and EXIT with `IMPLEMENT_RESULT: status: BLOCKED` (`blocker: migration collision`).
|
|
218
261
|
|
|
219
262
|
Stage the changed files:
|
|
220
263
|
```bash
|
|
@@ -272,7 +315,7 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:BUILDER -->
|
|
|
272
315
|
|
|
273
316
|
## Output
|
|
274
317
|
|
|
275
|
-
The subcommand writes its results to GitHub (FORGE:BUILDER comment).
|
|
318
|
+
The subcommand writes its results to GitHub (FORGE:BUILDER comment). The final reply is exactly one `IMPLEMENT_RESULT:` block — on every exit path (use `status: BLOCKED` + `blocker` for failures; fields not applicable may be empty):
|
|
276
319
|
|
|
277
320
|
```
|
|
278
321
|
IMPLEMENT_RESULT:
|
|
@@ -286,15 +329,16 @@ IMPLEMENT_RESULT:
|
|
|
286
329
|
|
|
287
330
|
---
|
|
288
331
|
|
|
289
|
-
## Integration Point
|
|
332
|
+
## Integration Point
|
|
290
333
|
|
|
291
|
-
This module
|
|
334
|
+
This module is invoked by `work-on:build` after the worktree exists and context/architect are posted, and before validate:
|
|
292
335
|
|
|
293
336
|
```
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
337
|
+
build (worktree + contract)
|
|
338
|
+
→ context (work-on:build:context)
|
|
339
|
+
→ architect (work-on:build:architect)
|
|
340
|
+
→ [THIS MODULE] implement — Phases I1–I6: code written, staged (not committed), FORGE:BUILDER posted
|
|
341
|
+
→ validate (work-on:build:validate) — gate loop; the single commit happens in validate Phase V5 after the gate passes
|
|
298
342
|
```
|
|
299
343
|
|
|
300
344
|
The validate subcommand reads the staged diff produced by this module, runs the gate, and commits only after the gate passes.
|