Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/dbt-tools/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import type { DBTProjectIntegrationAdapter, CommandProcessResult } from "@altima

export async function build(adapter: DBTProjectIntegrationAdapter, args: string[]) {
const model = flag(args, "model")
if (!model) return project(adapter)
const downstream = args.includes("--downstream")
if (!model) {
if (downstream) return { error: "--downstream requires --model" }
return project(adapter)
}
const result = await adapter.unsafeBuildModelImmediately({
plusOperatorLeft: "",
modelName: model,
Expand Down
8 changes: 8 additions & 0 deletions packages/dbt-tools/test/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ describe("build command", () => {
})
})

test("build --downstream without --model returns error", async () => {
const adapter = makeAdapter()
const result = await build(adapter, ["--downstream"])
expect(result).toEqual({ error: "--downstream requires --model" })
expect(adapter.unsafeBuildProjectImmediately).not.toHaveBeenCalled()
expect(adapter.unsafeBuildModelImmediately).not.toHaveBeenCalled()
})

test("build surfaces stderr as error", async () => {
const adapter = makeAdapter({
unsafeBuildProjectImmediately: mock(() =>
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/cli/cmd/tui/routes/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function Home() {
return Object.values(sync.data.mcp).filter((x) => x.status === "connected").length
})

// altimate_change start — fix race condition: don't show beginner UI until sessions loaded
// altimate_change start — upstream_fix: race condition shows beginner UI flash before sessions loaded
const isFirstTimeUser = createMemo(() => {
// Don't evaluate until sessions have actually loaded (avoid flash of beginner UI)
// Return undefined to represent "loading" state
Expand Down
5 changes: 3 additions & 2 deletions packages/opencode/src/command/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ export namespace Command {
const result: string[] = []
const numbered = template.match(/\$\d+/g)
if (numbered) {
// altimate_change start — fix lexicographic sort of multi-digit placeholders ($10 before $2)
for (const match of [...new Set(numbered)].sort((a, b) => parseInt(a.slice(1), 10) - parseInt(b.slice(1), 10))) result.push(match)
// altimate_change start — upstream_fix: lexicographic sort of multi-digit placeholders ($10 sorted before $2)
for (const match of [...new Set(numbered)].sort((a, b) => parseInt(a.slice(1), 10) - parseInt(b.slice(1), 10)))
result.push(match)
// altimate_change end
}
if (template.includes("$ARGUMENTS")) result.push("$ARGUMENTS")
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/skill/followups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export namespace SkillFollowups {
skill: string // skill name to suggest
label: string // short display label
description: string // why this is a good next step
condition?: string // optional: when this suggestion applies
}

// Map from skill name to follow-up suggestions
Expand Down Expand Up @@ -151,7 +150,8 @@ export namespace SkillFollowups {
}

// A special warehouse nudge for users who haven't connected yet
const WAREHOUSE_NUDGE = "**Tip:** Connect a warehouse to validate against real data. Run `/discover` to auto-detect your connections."
const WAREHOUSE_NUDGE =
"**Tip:** Connect a warehouse to validate against real data. Run `/discover` to auto-detect your connections."

export function get(skillName: string): readonly Suggestion[] {
return Object.freeze(FOLLOWUPS[skillName] ?? [])
Expand Down
2 changes: 2 additions & 0 deletions packages/opencode/src/util/locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ export namespace Locale {
const minutes = Math.floor((input % 3600000) / 60000)
return `${hours}h ${minutes}m`
}
// altimate_change start — upstream_fix: days/hours calculation were swapped (hours used total, not remainder)
const days = Math.floor(input / 86400000)
const hours = Math.floor((input % 86400000) / 3600000)
// altimate_change end
return `${days}d ${hours}h`
}

Expand Down
38 changes: 38 additions & 0 deletions script/upstream/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,44 @@ When we modify upstream files (not fully custom ones), we wrap our changes with

These help during conflict resolution — you can see exactly what we changed vs upstream code. The `analyze.ts` script audits for unclosed marker blocks.

### Upstream Bug Fixes (`upstream_fix:` tag)

When fixing a **bug in upstream code** (not adding a feature), use the `upstream_fix:` tag in the marker description:

```typescript
// altimate_change start — upstream_fix: days/hours calculation were swapped
const days = Math.floor(input / 86400000)
const hours = Math.floor((input % 86400000) / 3600000)
// altimate_change end
```

**Why this matters:** Regular `altimate_change` markers protect features we added — they're permanent. But upstream bug fixes are **temporary**: once upstream ships their own fix, we should drop our marker and accept theirs.

Without the `upstream_fix:` tag:
- If upstream fixes the same bug, the merge creates a conflict (good — forces review)
- But the reviewer doesn't know our change was a bug fix vs a feature, so they may keep both

With the `upstream_fix:` tag:
- Before each merge, run `--audit-fixes` to see all bug fixes we're carrying
- During conflict resolution, reviewers know to check "did upstream fix this?" and can safely drop our version
- After merge, any remaining `upstream_fix:` markers represent bugs upstream hasn't fixed yet

**When to use which:**

| Scenario | Marker |
|----------|--------|
| New feature/custom code | `// altimate_change start — description` |
| Fix bug in upstream code | `// altimate_change start — upstream_fix: description` |
| Branding change | No marker (handled by branding transforms) |
| Code in `keepOurs` files | No marker needed |

**Audit before merging:**

```bash
# List all upstream bug fixes we're carrying
bun run script/upstream/analyze.ts --audit-fixes
```

## File Organization

```
Expand Down
12 changes: 12 additions & 0 deletions script/upstream/analyze.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,18 @@ describe("parseDiffForMarkerWarnings", () => {
expect(warnings[0].context).toContain("first")
})

test("upstream_fix: tagged markers are recognized as valid markers", () => {
const diff = makeDiff(
`@@ -50,4 +50,6 @@
const existing = true
+// altimate_change start — upstream_fix: days/hours were swapped
+const days = Math.floor(input / 86400000)
+// altimate_change end
const more = true`,
)
expect(parseDiffForMarkerWarnings("file.ts", diff)).toEqual([])
})

test("real-world scenario: upgrade indicator in footer.tsx", () => {
// Simulates the exact diff that leaked: UpgradeIndicator added to
// session footer without markers, adjacent to existing yolo marker block.
Expand Down
116 changes: 90 additions & 26 deletions script/upstream/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const { values: args } = parseArgs({
version: { type: "string", short: "v" },
branding: { type: "boolean", default: false },
markers: { type: "boolean", default: false },
"audit-fixes": { type: "boolean", default: false },
strict: { type: "boolean", default: false },
base: { type: "string" },
verbose: { type: "boolean", default: false },
Expand Down Expand Up @@ -213,9 +214,7 @@ function printBrandingReport(report: BrandingReport, verbose: boolean): void {
const maxLeaksToShow = verbose ? leaks.length : 5
for (let i = 0; i < Math.min(leaks.length, maxLeaksToShow); i++) {
const leak = leaks[i]
const truncated = leak.content.length > 80
? leak.content.slice(0, 77) + "..."
: leak.content
const truncated = leak.content.length > 80 ? leak.content.slice(0, 77) + "..." : leak.content
console.log(` ${DIM}L${String(leak.line).padStart(4)}${RESET} ${YELLOW}${leak.pattern}${RESET}`)
console.log(` ${DIM}${truncated}${RESET}`)
}
Expand Down Expand Up @@ -298,14 +297,20 @@ async function analyzeVersion(version: string, config: MergeConfig): Promise<Ver
// Check for marker files and potential conflicts
for (const file of analysis.categories.transformable) {
try {
const content = await $`git show HEAD:${file}`.cwd(root).text().catch(() => "")
const content = await $`git show HEAD:${file}`
.cwd(root)
.text()
.catch(() => "")

if (content.includes(config.changeMarker)) {
analysis.markerFiles.push(file)
}

// Check if we've modified this file (potential conflict)
const ourDiff = await $`git diff HEAD -- ${file}`.cwd(root).text().catch(() => "")
const ourDiff = await $`git diff HEAD -- ${file}`
.cwd(root)
.text()
.catch(() => "")
if (ourDiff.trim().length > 0) {
analysis.potentialConflicts.push(file)
}
Expand Down Expand Up @@ -360,10 +365,16 @@ function printVersionAnalysis(analysis: VersionAnalysis): void {
const line = "─".repeat(50)
console.log(` ${line}`)
console.log(` ${bold("Merge estimate:")}`)
console.log(` Auto-resolvable: ${GREEN}${categories.keepOurs.length + categories.skipFiles.length + categories.lockFiles.length}${RESET}`)
console.log(
` Auto-resolvable: ${GREEN}${categories.keepOurs.length + categories.skipFiles.length + categories.lockFiles.length}${RESET}`,
)
console.log(` Need transform: ${categories.transformable.length}`)
console.log(` Likely conflicts: ${analysis.potentialConflicts.length > 0 ? RED : GREEN}${analysis.potentialConflicts.length}${RESET}`)
console.log(` Marker files: ${analysis.markerFiles.length > 0 ? YELLOW : GREEN}${analysis.markerFiles.length}${RESET}`)
console.log(
` Likely conflicts: ${analysis.potentialConflicts.length > 0 ? RED : GREEN}${analysis.potentialConflicts.length}${RESET}`,
)
console.log(
` Marker files: ${analysis.markerFiles.length > 0 ? YELLOW : GREEN}${analysis.markerFiles.length}${RESET}`,
)
console.log()
}

Expand Down Expand Up @@ -446,7 +457,9 @@ function printMarkerAnalysis(config: MergeConfig): void {
const complete = markers.filter((m) => m.endLine !== null)
const incomplete = markers.filter((m) => m.endLine === null)

console.log(` Found ${bold(String(markers.length))} marker blocks in ${new Set(markers.map((m) => m.file)).size} files`)
console.log(
` Found ${bold(String(markers.length))} marker blocks in ${new Set(markers.map((m) => m.file)).size} files`,
)
console.log(` ${GREEN}Complete (start + end):${RESET} ${complete.length}`)

if (incomplete.length > 0) {
Expand All @@ -468,10 +481,50 @@ function printMarkerAnalysis(config: MergeConfig): void {

// Summary
console.log()
console.log(` ${bold("Integrity:")} ${incomplete.length === 0
? `${GREEN}All blocks properly closed${RESET}`
: `${RED}${incomplete.length} unclosed block(s)${RESET}`
}`)
console.log(
` ${bold("Integrity:")} ${
incomplete.length === 0
? `${GREEN}All blocks properly closed${RESET}`
: `${RED}${incomplete.length} unclosed block(s)${RESET}`
}`,
)
}

// ---------------------------------------------------------------------------
// Upstream fix audit (--audit-fixes)
// ---------------------------------------------------------------------------

function auditUpstreamFixes(config: MergeConfig): void {
const markers = findMarkers(config)
const fixes = markers.filter((m) => m.startComment.includes("upstream_fix:"))

console.log()
console.log(bold("=== Upstream Bug Fixes We're Carrying ==="))
console.log()

if (fixes.length === 0) {
console.log(` ${GREEN}No upstream_fix: markers found.${RESET}`)
console.log(` All our markers are feature additions, not bug fixes.`)
console.log()
return
}

console.log(` Found ${bold(String(fixes.length))} upstream bug fix(es) to review before merge:\n`)

for (const fix of fixes) {
// Extract description after "upstream_fix:"
const desc = fix.startComment.replace(/.*upstream_fix:\s*/, "").replace(/\s*\*\/\s*$/, "")
const lines = fix.endLine ? `${fix.line}-${fix.endLine}` : `${fix.line}`
console.log(` ${YELLOW}fix${RESET} ${fix.file}:${lines}`)
console.log(` ${desc}`)
console.log()
}

console.log(` ${bold("Before each upstream merge:")}`)
console.log(` 1. Check if upstream fixed each issue in their release`)
console.log(` 2. If fixed upstream: accept their version, remove our marker`)
console.log(` 3. If not fixed: keep our marker (it will survive the merge)`)
console.log()
}

// ---------------------------------------------------------------------------
Expand All @@ -490,6 +543,7 @@ function printUsage(): void {
--version, -v <tag> Upstream version to analyze
--branding Scan codebase for upstream branding leaks
--markers Check changed files for missing altimate_change markers
--audit-fixes List all upstream_fix: markers (bug fixes we made to upstream code)
--base <branch> Base branch for --markers comparison (default: HEAD)
--strict Exit with code 1 on warnings (for CI)
--verbose Show all results (not just top 20)
Expand All @@ -509,6 +563,9 @@ function printUsage(): void {
${dim("# Check PR for missing markers (CI)")}
bun run script/upstream/analyze.ts --markers --base main --strict

${dim("# List upstream bug fixes we're carrying (review before merge)")}
bun run script/upstream/analyze.ts --audit-fixes

${dim("# Machine-readable output for CI")}
bun run script/upstream/analyze.ts --branding --json
`)
Expand All @@ -530,14 +587,9 @@ function getChangedFiles(base?: string): string[] {
const root = repoRoot()
// Only check Modified files (M), not Added (A). New files don't exist
// upstream so they can't be overwritten by a merge — no markers needed.
const cmd = base
? `git diff --name-only --diff-filter=M ${base}...HEAD`
: `git diff --name-only --diff-filter=M HEAD`
const cmd = base ? `git diff --name-only --diff-filter=M ${base}...HEAD` : `git diff --name-only --diff-filter=M HEAD`
try {
return execSync(cmd, { cwd: root, encoding: "utf-8" })
.trim()
.split("\n")
.filter(Boolean)
return execSync(cmd, { cwd: root, encoding: "utf-8" }).trim().split("\n").filter(Boolean)
} catch {
return []
}
Expand Down Expand Up @@ -646,8 +698,14 @@ export function parseDiffForMarkerWarnings(file: string, diffOutput: string): Ma
currentLine++
const content = line.slice(1).trim()

if (content.includes("altimate_change start")) { inMarkerBlock = true; continue }
if (content.includes("altimate_change end")) { inMarkerBlock = false; continue }
if (content.includes("altimate_change start")) {
inMarkerBlock = true
continue
}
if (content.includes("altimate_change end")) {
inMarkerBlock = false
continue
}
if (content.includes("altimate_change")) continue

// Only flag added lines as violations — context lines are pre-existing
Expand Down Expand Up @@ -686,9 +744,7 @@ function checkFileForMarkers(file: string, base?: string): MarkerWarning[] {
const { execSync } = require("child_process")
const root = repoRoot()

const diffCmd = base
? `git diff -U5 ${base}...HEAD -- "${file}"`
: `git diff -U5 HEAD -- "${file}"`
const diffCmd = base ? `git diff -U5 ${base}...HEAD -- "${file}"` : `git diff -U5 HEAD -- "${file}"`

let diffOutput: string
try {
Expand Down Expand Up @@ -770,18 +826,26 @@ async function main(): Promise<void> {
const hasVersion = Boolean(args.version)
const hasBranding = Boolean(args.branding)
const hasMarkers = Boolean(args.markers)
const hasAuditFixes = Boolean(args["audit-fixes"])

if (!hasVersion && !hasBranding && !hasMarkers) {
if (!hasVersion && !hasBranding && !hasMarkers && !hasAuditFixes) {
// Default: run marker analysis
printMarkerAnalysis(config)

console.log()
logger.info("Use --version <tag> to analyze an upstream version")
logger.info("Use --branding to audit for branding leaks")
logger.info("Use --markers --base main to check for missing markers")
logger.info("Use --audit-fixes to list upstream bug fixes we're carrying")
return
}

// ─── Upstream fix audit ──────────────────────────────────────────────────
if (hasAuditFixes) {
auditUpstreamFixes(config)
if (!hasVersion && !hasBranding && !hasMarkers) return
}

// ─── Version analysis ──────────────────────────────────────────────────────

if (hasVersion) {
Expand Down
Loading