@tyroneross/build-loop 0.30.3
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/.agents/plugins/marketplace.json +10 -0
- package/.claude-plugin/marketplace.json +38 -0
- package/.claude-plugin/plugin.json +30 -0
- package/.codex-plugin/plugin.json +41 -0
- package/.cursor/rules/build-loop-surface.mdc +22 -0
- package/AGENTS.md +498 -0
- package/LICENSE +202 -0
- package/README.md +403 -0
- package/agents/alignment-checker.md +124 -0
- package/agents/api-assessor.md +136 -0
- package/agents/architecture-scout.md +185 -0
- package/agents/assessment-orchestrator.md +163 -0
- package/agents/build-orchestrator.md +198 -0
- package/agents/database-assessor.md +133 -0
- package/agents/design-contract-specialist.md +239 -0
- package/agents/fact-checker.md +69 -0
- package/agents/fix-critique.md +161 -0
- package/agents/frontend-assessor.md +136 -0
- package/agents/implementer.md +185 -0
- package/agents/independent-auditor.md +143 -0
- package/agents/mock-scanner.md +78 -0
- package/agents/optimize-runner.md +112 -0
- package/agents/overfitting-reviewer.md +96 -0
- package/agents/performance-assessor.md +159 -0
- package/agents/plan-critic.md +85 -0
- package/agents/promotion-reviewer.md +121 -0
- package/agents/recurring-pattern-detector.md +248 -0
- package/agents/retrospective-synthesizer.md +109 -0
- package/agents/root-cause-investigator.md +285 -0
- package/agents/scope-auditor.md +121 -0
- package/agents/security-reviewer.md +151 -0
- package/agents/self-improvement-architect.md +125 -0
- package/agents/synthesis-critic.md +99 -0
- package/agents/transcript-pattern-miner.md +165 -0
- package/agents/ui-validator.md +193 -0
- package/codex-skills/build-loop/SKILL.md +20 -0
- package/codex-skills/debug-loop/SKILL.md +18 -0
- package/codex-skills/handoff/SKILL.md +22 -0
- package/codex-skills/knowledge/SKILL.md +21 -0
- package/codex-skills/optimize/SKILL.md +18 -0
- package/codex-skills/research/SKILL.md +18 -0
- package/commands/agent-rally-point.md +301 -0
- package/commands/assess.md +41 -0
- package/commands/debug.md +20 -0
- package/commands/debugger-detail.md +34 -0
- package/commands/debugger-scan.md +15 -0
- package/commands/debugger-status.md +20 -0
- package/commands/debugger.md +32 -0
- package/commands/handoff.md +113 -0
- package/commands/knowledge-review.md +16 -0
- package/commands/optimize.md +31 -0
- package/commands/plan-verify.md +39 -0
- package/commands/promote-experiment.md +129 -0
- package/commands/research.md +22 -0
- package/commands/run.md +12 -0
- package/commands/self-review.md +51 -0
- package/commands/start-prd.md +57 -0
- package/commands/test.md +34 -0
- package/dist/src/assessment-orchestrator.d.ts +78 -0
- package/dist/src/assessment-orchestrator.d.ts.map +1 -0
- package/dist/src/assessment-orchestrator.js +304 -0
- package/dist/src/assessment-orchestrator.js.map +1 -0
- package/dist/src/audit-miner.d.ts +21 -0
- package/dist/src/audit-miner.d.ts.map +1 -0
- package/dist/src/audit-miner.js +421 -0
- package/dist/src/audit-miner.js.map +1 -0
- package/dist/src/batch-operations.d.ts +30 -0
- package/dist/src/batch-operations.d.ts.map +1 -0
- package/dist/src/batch-operations.js +339 -0
- package/dist/src/batch-operations.js.map +1 -0
- package/dist/src/config.d.ts +83 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +176 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/context-engine.d.ts +45 -0
- package/dist/src/context-engine.d.ts.map +1 -0
- package/dist/src/context-engine.js +135 -0
- package/dist/src/context-engine.js.map +1 -0
- package/dist/src/debug-wrapper.d.ts +58 -0
- package/dist/src/debug-wrapper.d.ts.map +1 -0
- package/dist/src/debug-wrapper.js +285 -0
- package/dist/src/debug-wrapper.js.map +1 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +149 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/interactive-verifier.d.ts +26 -0
- package/dist/src/interactive-verifier.d.ts.map +1 -0
- package/dist/src/interactive-verifier.js +435 -0
- package/dist/src/interactive-verifier.js.map +1 -0
- package/dist/src/lessons.d.ts +59 -0
- package/dist/src/lessons.d.ts.map +1 -0
- package/dist/src/lessons.js +172 -0
- package/dist/src/lessons.js.map +1 -0
- package/dist/src/log-reader.d.ts +39 -0
- package/dist/src/log-reader.d.ts.map +1 -0
- package/dist/src/log-reader.js +291 -0
- package/dist/src/log-reader.js.map +1 -0
- package/dist/src/logger.d.ts +59 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +328 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/parallel-retrieval.d.ts +52 -0
- package/dist/src/parallel-retrieval.d.ts.map +1 -0
- package/dist/src/parallel-retrieval.js +309 -0
- package/dist/src/parallel-retrieval.js.map +1 -0
- package/dist/src/pattern-extractor.d.ts +30 -0
- package/dist/src/pattern-extractor.d.ts.map +1 -0
- package/dist/src/pattern-extractor.js +386 -0
- package/dist/src/pattern-extractor.js.map +1 -0
- package/dist/src/result-aggregator.d.ts +44 -0
- package/dist/src/result-aggregator.d.ts.map +1 -0
- package/dist/src/result-aggregator.js +315 -0
- package/dist/src/result-aggregator.js.map +1 -0
- package/dist/src/retrieval.d.ts +139 -0
- package/dist/src/retrieval.d.ts.map +1 -0
- package/dist/src/retrieval.js +724 -0
- package/dist/src/retrieval.js.map +1 -0
- package/dist/src/setup/auto-setup.d.ts +15 -0
- package/dist/src/setup/auto-setup.d.ts.map +1 -0
- package/dist/src/setup/auto-setup.js +157 -0
- package/dist/src/setup/auto-setup.js.map +1 -0
- package/dist/src/setup/configure-hooks.d.ts +8 -0
- package/dist/src/setup/configure-hooks.d.ts.map +1 -0
- package/dist/src/setup/configure-hooks.js +16 -0
- package/dist/src/setup/configure-hooks.js.map +1 -0
- package/dist/src/setup/create-slash-commands.d.ts +8 -0
- package/dist/src/setup/create-slash-commands.d.ts.map +1 -0
- package/dist/src/setup/create-slash-commands.js +101 -0
- package/dist/src/setup/create-slash-commands.js.map +1 -0
- package/dist/src/setup/index.d.ts +8 -0
- package/dist/src/setup/index.d.ts.map +1 -0
- package/dist/src/setup/index.js +17 -0
- package/dist/src/setup/index.js.map +1 -0
- package/dist/src/setup/inject-claude-md.d.ts +10 -0
- package/dist/src/setup/inject-claude-md.d.ts.map +1 -0
- package/dist/src/setup/inject-claude-md.js +150 -0
- package/dist/src/setup/inject-claude-md.js.map +1 -0
- package/dist/src/setup/uninstall.d.ts +22 -0
- package/dist/src/setup/uninstall.d.ts.map +1 -0
- package/dist/src/setup/uninstall.js +235 -0
- package/dist/src/setup/uninstall.js.map +1 -0
- package/dist/src/storage.d.ts +192 -0
- package/dist/src/storage.d.ts.map +1 -0
- package/dist/src/storage.js +1037 -0
- package/dist/src/storage.js.map +1 -0
- package/dist/src/string-similarity.d.ts +8 -0
- package/dist/src/string-similarity.d.ts.map +1 -0
- package/dist/src/string-similarity.js +57 -0
- package/dist/src/string-similarity.js.map +1 -0
- package/dist/src/traces/adapters/browser.d.ts +69 -0
- package/dist/src/traces/adapters/browser.d.ts.map +1 -0
- package/dist/src/traces/adapters/browser.js +280 -0
- package/dist/src/traces/adapters/browser.js.map +1 -0
- package/dist/src/traces/adapters/index.d.ts +23 -0
- package/dist/src/traces/adapters/index.d.ts.map +1 -0
- package/dist/src/traces/adapters/index.js +67 -0
- package/dist/src/traces/adapters/index.js.map +1 -0
- package/dist/src/traces/adapters/langchain.d.ts +42 -0
- package/dist/src/traces/adapters/langchain.d.ts.map +1 -0
- package/dist/src/traces/adapters/langchain.js +151 -0
- package/dist/src/traces/adapters/langchain.js.map +1 -0
- package/dist/src/traces/adapters/opentelemetry.d.ts +46 -0
- package/dist/src/traces/adapters/opentelemetry.d.ts.map +1 -0
- package/dist/src/traces/adapters/opentelemetry.js +164 -0
- package/dist/src/traces/adapters/opentelemetry.js.map +1 -0
- package/dist/src/traces/adapters/sentry.d.ts +58 -0
- package/dist/src/traces/adapters/sentry.d.ts.map +1 -0
- package/dist/src/traces/adapters/sentry.js +212 -0
- package/dist/src/traces/adapters/sentry.js.map +1 -0
- package/dist/src/traces/index.d.ts +10 -0
- package/dist/src/traces/index.d.ts.map +1 -0
- package/dist/src/traces/index.js +45 -0
- package/dist/src/traces/index.js.map +1 -0
- package/dist/src/traces/storage.d.ts +78 -0
- package/dist/src/traces/storage.d.ts.map +1 -0
- package/dist/src/traces/storage.js +399 -0
- package/dist/src/traces/storage.js.map +1 -0
- package/dist/src/traces/summarizer.d.ts +83 -0
- package/dist/src/traces/summarizer.d.ts.map +1 -0
- package/dist/src/traces/summarizer.js +247 -0
- package/dist/src/traces/summarizer.js.map +1 -0
- package/dist/src/traces/types.d.ts +365 -0
- package/dist/src/traces/types.d.ts.map +1 -0
- package/dist/src/traces/types.js +14 -0
- package/dist/src/traces/types.js.map +1 -0
- package/dist/src/types.d.ts +495 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +27 -0
- package/dist/src/types.js.map +1 -0
- package/docs/agent-surface-policy.md +45 -0
- package/docs/memory-setup.md +226 -0
- package/hooks/_arch_scan_bg.py +88 -0
- package/hooks/_plugin_drift_check_bg.py +85 -0
- package/hooks/_session_start_lib.sh +42 -0
- package/hooks/git/pre-push +141 -0
- package/hooks/hooks.json +126 -0
- package/hooks/post-commit +30 -0
- package/hooks/pre-commit +29 -0
- package/hooks/pre-edit-apppulse.sh +6 -0
- package/hooks/pre-edit-architecture.sh +106 -0
- package/hooks/pre-edit-rally-point.sh +92 -0
- package/hooks/session-start-apppulse.sh +6 -0
- package/hooks/session-start-architecture.sh +10 -0
- package/hooks/session-start-git-hooks.sh +12 -0
- package/hooks/session-start-plugin-drift.sh +27 -0
- package/hooks/session-start-rally-point.sh +44 -0
- package/hooks/session-start-retrieval.sh +24 -0
- package/hooks/session-start-worktree-gc.sh +105 -0
- package/hooks/test_apppulse_hooks.sh +6 -0
- package/hooks/test_rally_point_hooks.sh +388 -0
- package/hooks/test_sec008_hooks.sh +66 -0
- package/package.json +111 -0
- package/scripts/README.md +301 -0
- package/scripts/_db_url.py +61 -0
- package/scripts/_paths.py +445 -0
- package/scripts/blm.py +162 -0
- package/scripts/blm_api.py +263 -0
- package/scripts/check_cache_sync.py +490 -0
- package/scripts/embed_backend.py +475 -0
- package/scripts/install_memory.py +556 -0
- package/scripts/lessons_index/__init__.py +77 -0
- package/scripts/lessons_index/__main__.py +135 -0
- package/scripts/lessons_index/ingest.py +288 -0
- package/scripts/lessons_index/query.py +285 -0
- package/scripts/lessons_index/schema.py +114 -0
- package/scripts/memory_context/__init__.py +610 -0
- package/scripts/memory_graph/__init__.py +393 -0
- package/scripts/project_resolver.py +172 -0
- package/scripts/prune_codex_plugin_cache.py +23 -0
- package/scripts/prune_plugin_cache.py +359 -0
- package/scripts/semantic_index/__init__.py +482 -0
- package/scripts/semantic_index/_bench_hybrid.py +175 -0
- package/scripts/semantic_index/backfill.py +263 -0
- package/scripts/semantic_index/hybrid.py +174 -0
- package/scripts/semantic_index/test_backfill.py +292 -0
- package/scripts/semantic_index/test_hybrid.py +541 -0
- package/scripts/sync_navgator_lessons.py +666 -0
- package/scripts/sync_plugin_cache.py +388 -0
- package/skills/agent-rally-point/SKILL.md +165 -0
- package/skills/agent-rally-watcher/SKILL.md +83 -0
- package/skills/api-registry-bridge/SKILL.md +59 -0
- package/skills/architecture/dead/SKILL.md +56 -0
- package/skills/architecture/impact/SKILL.md +88 -0
- package/skills/architecture/review/SKILL.md +227 -0
- package/skills/architecture/rules/SKILL.md +72 -0
- package/skills/architecture/scan/SKILL.md +79 -0
- package/skills/architecture/trace/SKILL.md +65 -0
- package/skills/attribution-standard/SKILL.md +122 -0
- package/skills/authentication/SKILL.md +81 -0
- package/skills/authentication/references/better-auth-magic-link.md +135 -0
- package/skills/authentication/references/better-auth-setup.md +224 -0
- package/skills/authentication/references/google-calendar-sync.md +278 -0
- package/skills/authentication/references/google-cloud-console.md +159 -0
- package/skills/authentication/references/google-geocoding-directions.md +126 -0
- package/skills/authentication/references/google-lessons-example-web-app.md +177 -0
- package/skills/authentication/references/google-maps.md +162 -0
- package/skills/authentication/references/google-oauth-setup.md +343 -0
- package/skills/authentication/references/google-places.md +173 -0
- package/skills/authentication/references/lessons-example-web-app-better-auth.md +126 -0
- package/skills/authentication/references/resend-email.md +227 -0
- package/skills/authentication/references/resend-otp-magic-link.md +153 -0
- package/skills/authentication/references/supabase-auth.md +172 -0
- package/skills/auto-decision-capture/SKILL.md +419 -0
- package/skills/build-loop/SKILL.md +516 -0
- package/skills/build-loop/detect-plugins.mjs +82 -0
- package/skills/build-loop/eval-guide.md +65 -0
- package/skills/build-loop/fallbacks.md +549 -0
- package/skills/build-loop/phases/fact-check.md +42 -0
- package/skills/build-loop/phases/ui-validation.md +267 -0
- package/skills/build-loop/references/apple-native-planning.md +439 -0
- package/skills/build-loop/references/capability-routing.md +181 -0
- package/skills/build-loop/references/codex-subagents.md +98 -0
- package/skills/build-loop/references/coordination.md +161 -0
- package/skills/build-loop/references/correction-aware-capture.md +177 -0
- package/skills/build-loop/references/independent-auditor.md +72 -0
- package/skills/build-loop/references/intent-capability-pack.md +170 -0
- package/skills/build-loop/references/intent-exploration-prompts.md +96 -0
- package/skills/build-loop/references/leadership.md +72 -0
- package/skills/build-loop/references/memory.md +284 -0
- package/skills/build-loop/references/modular-systems-pack.md +96 -0
- package/skills/build-loop/references/output-style.md +129 -0
- package/skills/build-loop/references/pay-it-forward-arch.md +98 -0
- package/skills/build-loop/references/phase-1-assess.md +218 -0
- package/skills/build-loop/references/phase-2-plan.md +77 -0
- package/skills/build-loop/references/phase-3-execute.md +47 -0
- package/skills/build-loop/references/phase-4-review.md +310 -0
- package/skills/build-loop/references/phase-5-iterate.md +71 -0
- package/skills/build-loop/references/phase-6-learn.md +56 -0
- package/skills/build-loop/references/recent-design-structures.md +274 -0
- package/skills/build-loop/references/refactor-history/ASSESSMENT.md +85 -0
- package/skills/build-loop/references/refactor-history/STANDALONE_TEST_RUN.md +149 -0
- package/skills/build-loop/references/refactor-history/scenarios/01-simple-bugfix.md +32 -0
- package/skills/build-loop/references/refactor-history/scenarios/02-ui-build-with-iteration.md +48 -0
- package/skills/build-loop/references/refactor-history/scenarios/03-multi-failure-escalation.md +60 -0
- package/skills/build-loop/references/refactor-history/scenarios/04-ui-build-ibr-absent.md +51 -0
- package/skills/build-loop/references/refactor-history/scenarios/05-refactor-navgator-absent.md +71 -0
- package/skills/build-loop/references/refactor-history/scenarios/06-recurring-bug-debugger-absent.md +52 -0
- package/skills/build-loop/references/refactor-history/trace-comparison.md +202 -0
- package/skills/build-loop/references/self-recursive-dev.md +77 -0
- package/skills/build-loop/references/self-review.md +232 -0
- package/skills/build-loop/references/ui-io-contract.md +116 -0
- package/skills/build-loop/scanners/audit-design-rules.mjs +476 -0
- package/skills/build-loop/scanners/require-visual-evidence.mjs +239 -0
- package/skills/build-loop/templates/backlog-item.md +35 -0
- package/skills/build-loop/templates/codex-worker-prompt.md +89 -0
- package/skills/build-loop/templates/ui-subagent-prompt.md +179 -0
- package/skills/build-loop/templates/ux-fix-plan.md +40 -0
- package/skills/building-with-deepagents/SKILL.md +195 -0
- package/skills/building-with-deepagents/references/anti-patterns.md +141 -0
- package/skills/building-with-deepagents/references/api-quick-reference.md +184 -0
- package/skills/capabilities/SKILL.md +91 -0
- package/skills/debug-loop/SKILL.md +278 -0
- package/skills/debug-loop/references/convergence-rules.md +145 -0
- package/skills/debug-loop/references/swiftui-macos-clunky-ui-debugger.html +823 -0
- package/skills/debugging/assess/SKILL.md +111 -0
- package/skills/debugging/debug-loop/SKILL.md +231 -0
- package/skills/debugging/memory/SKILL.md +147 -0
- package/skills/debugging/store/SKILL.md +153 -0
- package/skills/debugging-memory/SKILL.md +440 -0
- package/skills/debugging-memory/examples/incident-example.json +67 -0
- package/skills/debugging-memory/references/incident-documentation.md +280 -0
- package/skills/debugging-memory/references/ios-notification-alarm-playbook.md +300 -0
- package/skills/debugging-memory/references/pattern-extraction.md +163 -0
- package/skills/debugging-memory/references/subagent-integration.md +312 -0
- package/skills/defenseclaw-bridge/SKILL.md +156 -0
- package/skills/defenseclaw-bridge/references/dc-config-mapping.md +162 -0
- package/skills/defenseclaw-bridge/references/output-format.md +108 -0
- package/skills/handoff/SKILL.md +94 -0
- package/skills/ibr-bridge/SKILL.md +68 -0
- package/skills/knowledge/SKILL.md +127 -0
- package/skills/knowledge/references/recall-integration.md +83 -0
- package/skills/knowledge/templates/madr-minimal.md +56 -0
- package/skills/knowledge-review/SKILL.md +112 -0
- package/skills/logging-tracer/SKILL.md +202 -0
- package/skills/logging-tracer/references/log-analysis.md +128 -0
- package/skills/logging-tracer/references/stack-templates.md +274 -0
- package/skills/logging-tracer-bridge/SKILL.md +70 -0
- package/skills/mcp-builder/SKILL.md +278 -0
- package/skills/mcp-builder/references/mcp-security.md +106 -0
- package/skills/model-tiering/SKILL.md +216 -0
- package/skills/native-ax-driver/SKILL.md +220 -0
- package/skills/native-ax-driver/scripts/layout_fill.py +222 -0
- package/skills/native-ax-driver/scripts/native_driver.py +495 -0
- package/skills/native-ax-driver/swift/bl-ax-driver/Package.swift +20 -0
- package/skills/native-ax-driver/swift/bl-ax-driver/Sources/main.swift +542 -0
- package/skills/optimize/SKILL.md +202 -0
- package/skills/optimize/profiles.md +61 -0
- package/skills/plan-verify/SKILL.md +115 -0
- package/skills/plan-verify/test-fixtures/example-app-v20-findings.json +27 -0
- package/skills/plan-verify/test-fixtures/example-app-v20.md +42 -0
- package/skills/plan-verify/test-fixtures/example-app-v22-findings.json +15 -0
- package/skills/plan-verify/test-fixtures/example-app-v22.md +1608 -0
- package/skills/plan-verify/test-fixtures/unrelated-good-plan-findings.json +15 -0
- package/skills/plan-verify/test-fixtures/unrelated-good-plan.md +55 -0
- package/skills/plugin-builder/SKILL.md +329 -0
- package/skills/plugin-builder/references/authoritative-sources.md +104 -0
- package/skills/plugin-builder/references/build-loop-phase-guidance.md +94 -0
- package/skills/plugin-builder/references/components-guide.md +221 -0
- package/skills/plugin-builder/references/distribution.md +317 -0
- package/skills/plugin-builder/references/dual-host-claude-codex.md +220 -0
- package/skills/plugin-builder/references/hooks-reference.md +210 -0
- package/skills/plugin-builder/references/manifest-schema.md +111 -0
- package/skills/plugin-builder/references/plugin-hygiene-lessons.md +307 -0
- package/skills/plugin-tests/SKILL.md +94 -0
- package/skills/prd-bridge/SKILL.md +135 -0
- package/skills/research/SKILL.md +80 -0
- package/skills/security-methodology/SKILL.md +105 -0
- package/skills/security-methodology/references/cross-source-matrix.md +77 -0
- package/skills/security-methodology/references/mitre-atlas-starter.md +83 -0
- package/skills/security-methodology/references/nist-600-1-mapping.md +119 -0
- package/skills/security-methodology/references/owasp-agentic-top-10.md +157 -0
- package/skills/security-methodology/references/owasp-llm-top-10.md +144 -0
- package/skills/security-methodology/references/owasp-web-top-10.md +65 -0
- package/skills/self-improve/SKILL.md +298 -0
- package/skills/spec-writing/SKILL.md +548 -0
- package/skills/spec-writing/scripts/check_checklist.py +478 -0
- package/skills/sync-skills/SKILL.md +132 -0
- package/skills/telemetry/SKILL.md +66 -0
- package/skills/ui-design/SKILL.md +75 -0
- package/skills/ui-design/references/design-patterns-multi.md +165 -0
- package/skills/ui-design/references/design-preferences-from-owned-apps.md +150 -0
- package/skills/ui-design/references/evidence-capture-policy.md +55 -0
- package/skills/ui-design/references/ui-guidance-sources.md +330 -0
- package/skills/ui-design/references/universal-design-principles.alt.md +182 -0
- package/skills/ui-design/references/universal-design-principles.md +419 -0
- package/templates/memory/MEMORY.md.template +55 -0
- package/templates/memory/constitution.md.template +64 -0
- package/templates/memory/manifest.json +47 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
# Fixture-driven tests for the Rally Point hooks:
|
|
5
|
+
# - session-start-rally-point.sh : one-line restore only on delta,
|
|
6
|
+
# silent + exit 0 otherwise
|
|
7
|
+
# - pre-edit-rally-point.sh : cheap revision-stat hint, never blocks
|
|
8
|
+
# Any failure exits non-zero.
|
|
9
|
+
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
14
|
+
SS_HOOK="${SCRIPT_DIR}/session-start-rally-point.sh"
|
|
15
|
+
PE_HOOK="${SCRIPT_DIR}/pre-edit-rally-point.sh"
|
|
16
|
+
PKG="${REPO_ROOT}/scripts/rally_point"
|
|
17
|
+
|
|
18
|
+
PASS=0
|
|
19
|
+
FAIL=0
|
|
20
|
+
pass() { echo "PASS: $1"; PASS=$((PASS + 1)); }
|
|
21
|
+
fail() { echo "FAIL: $1 — $2"; FAIL=$((FAIL + 1)); }
|
|
22
|
+
|
|
23
|
+
# Build a temp git repo (so channel_paths.app_slug resolves) + isolated
|
|
24
|
+
# apps root.
|
|
25
|
+
REPO=$(mktemp -d)
|
|
26
|
+
APPS=$(mktemp -d)
|
|
27
|
+
( cd "$REPO" && git init -q && git config user.email t@e.com \
|
|
28
|
+
&& git config user.name t )
|
|
29
|
+
export BUILD_LOOP_APPS_ROOT="$APPS"
|
|
30
|
+
export BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1
|
|
31
|
+
export BUILD_LOOP_RALLY_POINT_SKIP_WATCH=1
|
|
32
|
+
|
|
33
|
+
# ---- Case 1: no channel yet -> SessionStart silent, exit 0 -------------
|
|
34
|
+
OUT=$(CLAUDE_PROJECT_DIR="$REPO" bash "$SS_HOOK" </dev/null 2>/dev/null)
|
|
35
|
+
RC=$?
|
|
36
|
+
if [ "$RC" -eq 0 ] && [ -z "$OUT" ]; then
|
|
37
|
+
pass "SessionStart silent + exit 0 when no channel"
|
|
38
|
+
else
|
|
39
|
+
fail "SessionStart no-channel" "rc=$RC out='$OUT'"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# ---- Case 2: pre-edit never blocks, exit 0, joins and prints when in a repo
|
|
43
|
+
# (the join fires immediately on first tool-use for a git repo with no prior
|
|
44
|
+
# channel — this is the "newly joined" path, not a "no channel = silence" path).
|
|
45
|
+
OUT=$(CLAUDE_PROJECT_DIR="$REPO" bash "$PE_HOOK" </dev/null 2>/dev/null)
|
|
46
|
+
RC=$?
|
|
47
|
+
if [ "$RC" -eq 0 ]; then
|
|
48
|
+
pass "pre-edit exit 0 when no prior channel (join fires or no-op)"
|
|
49
|
+
else
|
|
50
|
+
fail "pre-edit no-channel" "rc=$RC out='$OUT'"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# ---- Seed a delta: write a change + bump revision via the modules -----
|
|
54
|
+
REPO="$REPO" APPS="$APPS" PKG="$PKG" python3 - <<'PYEOF'
|
|
55
|
+
import os, sys
|
|
56
|
+
sys.path.insert(0, os.environ["PKG"])
|
|
57
|
+
import channel_paths as ap, changes as ch, revision as rev, presence as pr
|
|
58
|
+
slug = ap.app_slug(cwd=os.environ["REPO"])
|
|
59
|
+
chan = ap.ensure_channel_dir(slug)
|
|
60
|
+
pr.write_presence(chan, session_id="peerX", tool="codex", model="m",
|
|
61
|
+
run_id="rp", app_slug=slug, phase="execute",
|
|
62
|
+
files_in_flight=["a.py"])
|
|
63
|
+
ch.append_change(chan, ch.make_record(
|
|
64
|
+
kind="dep-change", tool="codex", model="m", run_id="rp",
|
|
65
|
+
app_slug=slug, payload={"manifests": ["package.json"]}, revision=1))
|
|
66
|
+
rev.bump_revision(chan)
|
|
67
|
+
PYEOF
|
|
68
|
+
|
|
69
|
+
# ---- Case 3: pre-edit prints peer-ahead hint when revision advanced ----
|
|
70
|
+
# Throttle is 60s (default); the Case 2 join wrote a presence record moments
|
|
71
|
+
# ago so the join is throttled — only the hint fires.
|
|
72
|
+
OUT=$(CLAUDE_PROJECT_DIR="$REPO" bash "$PE_HOOK" </dev/null 2>/dev/null)
|
|
73
|
+
RC=$?
|
|
74
|
+
if [ "$RC" -eq 0 ] && echo "$OUT" | grep -q "peer ahead"; then
|
|
75
|
+
pass "pre-edit prints 'peer ahead' hint when revision advanced"
|
|
76
|
+
else
|
|
77
|
+
fail "pre-edit hint" "rc=$RC out='$OUT'"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# ---- Case 4: SessionStart prints exactly one restore line on delta -----
|
|
81
|
+
OUT=$(CLAUDE_PROJECT_DIR="$REPO" bash "$SS_HOOK" </dev/null 2>/dev/null)
|
|
82
|
+
RC=$?
|
|
83
|
+
LINES=$(printf '%s' "$OUT" | grep -c . || true)
|
|
84
|
+
if [ "$RC" -eq 0 ] && [ "$LINES" -eq 1 ] \
|
|
85
|
+
&& echo "$OUT" | grep -q "Rally Point:" \
|
|
86
|
+
&& echo "$OUT" | grep -q "reinstall"; then
|
|
87
|
+
pass "SessionStart one-line restore on delta"
|
|
88
|
+
else
|
|
89
|
+
fail "SessionStart restore" "rc=$RC lines=$LINES out='$OUT'"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# ---- Case 5: SessionStart silent on the SECOND call (cursor advanced) --
|
|
93
|
+
OUT=$(CLAUDE_PROJECT_DIR="$REPO" bash "$SS_HOOK" </dev/null 2>/dev/null)
|
|
94
|
+
RC=$?
|
|
95
|
+
if [ "$RC" -eq 0 ] && [ -z "$OUT" ]; then
|
|
96
|
+
pass "SessionStart silent after cursor advanced (delta-only)"
|
|
97
|
+
else
|
|
98
|
+
fail "SessionStart second-call" "rc=$RC out='$OUT'"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# =========================================================================
|
|
102
|
+
# Codex-parity autojoin tests (2026-06-07)
|
|
103
|
+
# =========================================================================
|
|
104
|
+
|
|
105
|
+
# Per-repo isolation: ALL new cases write into their own APPS subdir so
|
|
106
|
+
# they cannot read state from the cases above. Use NONREPO + REPO_X.
|
|
107
|
+
NONREPO=$(mktemp -d)
|
|
108
|
+
REPO_X=$(mktemp -d)
|
|
109
|
+
( cd "$REPO_X" && git init -q && git config user.email t@e.com \
|
|
110
|
+
&& git config user.name t )
|
|
111
|
+
APPS2=$(mktemp -d)
|
|
112
|
+
|
|
113
|
+
# macOS realpath: /var/folders/... -> /private/var/folders/... — the
|
|
114
|
+
# operative-repo resolver runs `git rev-parse --show-toplevel` which
|
|
115
|
+
# returns the resolved form, so compare against that.
|
|
116
|
+
REPO_X_REAL=$(python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$REPO_X")
|
|
117
|
+
|
|
118
|
+
# Force a short throttle window so the throttle test does not have to
|
|
119
|
+
# sleep for the full 60s default.
|
|
120
|
+
export BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=5
|
|
121
|
+
|
|
122
|
+
# ---- Case 6: SessionStart no-misregister when CLAUDE_PROJECT_DIR unset
|
|
123
|
+
# AND $PWD is not a repo (home-like scenario). The hook must exit 0 and
|
|
124
|
+
# write NOTHING under APPS2.
|
|
125
|
+
BUILD_LOOP_APPS_ROOT="$APPS2" \
|
|
126
|
+
bash -c "cd '$NONREPO' && unset CLAUDE_PROJECT_DIR && BUILD_LOOP_APPS_ROOT='$APPS2' BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 BUILD_LOOP_RALLY_POINT_SKIP_WATCH=1 bash '$SS_HOOK'" </dev/null >/tmp/_ss_nonrepo.out 2>/dev/null
|
|
127
|
+
RC=$?
|
|
128
|
+
WROTE=$(find "$APPS2" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
129
|
+
if [ "$RC" -eq 0 ] && [ "$WROTE" -eq 0 ]; then
|
|
130
|
+
pass "SessionStart no-misregister when launch cwd is not a repo"
|
|
131
|
+
else
|
|
132
|
+
fail "SessionStart no-misregister" "rc=$RC wrote=$WROTE out=$(cat /tmp/_ss_nonrepo.out 2>/dev/null)"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Reset APPS2 between cases (each new case starts clean).
|
|
136
|
+
rm -rf "$APPS2" && mkdir -p "$APPS2"
|
|
137
|
+
|
|
138
|
+
# ---- Case 7: pre-edit Edit/Write joins room of the OPERATIVE repo,
|
|
139
|
+
# not the launch dir. We invoke from NONREPO but the file_path lives
|
|
140
|
+
# inside REPO_X. Presence must land under REPO_X's slug.
|
|
141
|
+
EVENT_JSON=$(printf '{"tool_input":{"file_path":"%s/a.py"}}' "$REPO_X")
|
|
142
|
+
BUILD_LOOP_APPS_ROOT="$APPS2" BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
143
|
+
bash -c "cd '$NONREPO' && unset CLAUDE_PROJECT_DIR && \
|
|
144
|
+
printf '%s' '$EVENT_JSON' | \
|
|
145
|
+
BUILD_LOOP_APPS_ROOT='$APPS2' BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
146
|
+
BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=5 \
|
|
147
|
+
bash '$PE_HOOK'" >/tmp/_pe_edit.out 2>/dev/null
|
|
148
|
+
RC=$?
|
|
149
|
+
REPO_X_SLUG=$(basename "$REPO_X" | tr '[:upper:]' '[:lower:]')
|
|
150
|
+
PRESENCE_COUNT=$(find "$APPS2" -path "*/$REPO_X_SLUG/sessions/*.json" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
151
|
+
# Allow normalized slug variants (the slug normalizer maps unsafe chars).
|
|
152
|
+
ANY_PRESENCE=$(find "$APPS2" -path "*/sessions/*.json" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
153
|
+
if [ "$RC" -eq 0 ] && [ "$ANY_PRESENCE" -ge 1 ]; then
|
|
154
|
+
# Verify the presence record's cwd points into REPO_X, not NONREPO.
|
|
155
|
+
CWD_HIT=$(grep -l "\"cwd\":\"$REPO_X_REAL\"" "$APPS2"/*/sessions/*.json 2>/dev/null | head -1)
|
|
156
|
+
if [ -n "$CWD_HIT" ]; then
|
|
157
|
+
pass "pre-edit Edit joins operative repo room (not launch dir)"
|
|
158
|
+
else
|
|
159
|
+
fail "pre-edit Edit operative-repo cwd" "rc=$RC any_presence=$ANY_PRESENCE files=$(ls "$APPS2"/*/sessions/ 2>/dev/null)"
|
|
160
|
+
fi
|
|
161
|
+
else
|
|
162
|
+
fail "pre-edit Edit operative-repo" "rc=$RC any_presence=$ANY_PRESENCE out=$(cat /tmp/_pe_edit.out 2>/dev/null)"
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# Reset between cases.
|
|
166
|
+
rm -rf "$APPS2" && mkdir -p "$APPS2"
|
|
167
|
+
|
|
168
|
+
# ---- Case 8: pre-edit Bash with leading `cd <repoX>` resolves repoX
|
|
169
|
+
# from the command payload, even when the launch cwd is NONREPO.
|
|
170
|
+
EVENT_JSON=$(printf '{"tool_input":{"command":"cd %s && echo hi"}}' "$REPO_X")
|
|
171
|
+
bash -c "cd '$NONREPO' && unset CLAUDE_PROJECT_DIR && \
|
|
172
|
+
printf '%s' '$EVENT_JSON' | \
|
|
173
|
+
BUILD_LOOP_APPS_ROOT='$APPS2' BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
174
|
+
BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=5 \
|
|
175
|
+
bash '$PE_HOOK'" >/tmp/_pe_bash.out 2>/dev/null
|
|
176
|
+
RC=$?
|
|
177
|
+
ANY_PRESENCE=$(find "$APPS2" -path "*/sessions/*.json" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
178
|
+
if [ "$RC" -eq 0 ] && [ "$ANY_PRESENCE" -ge 1 ]; then
|
|
179
|
+
CWD_HIT=$(grep -l "\"cwd\":\"$REPO_X_REAL\"" "$APPS2"/*/sessions/*.json 2>/dev/null | head -1)
|
|
180
|
+
if [ -n "$CWD_HIT" ]; then
|
|
181
|
+
pass "pre-edit Bash 'cd <repoX>' resolves operative repo"
|
|
182
|
+
else
|
|
183
|
+
fail "pre-edit Bash cwd" "rc=$RC files=$(ls "$APPS2"/*/sessions/ 2>/dev/null)"
|
|
184
|
+
fi
|
|
185
|
+
else
|
|
186
|
+
fail "pre-edit Bash operative-repo" "rc=$RC any_presence=$ANY_PRESENCE out=$(cat /tmp/_pe_bash.out 2>/dev/null)"
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
# DO NOT reset between Case 8 and Case 9 — Case 9 verifies the throttle
|
|
190
|
+
# blocks a SECOND write within the throttle window.
|
|
191
|
+
|
|
192
|
+
# ---- Case 9: Throttle: a second rapid tool-use in the same room within
|
|
193
|
+
# the throttle window must NOT write a fresh presence file AND must NOT
|
|
194
|
+
# create a new presence file (each tool-use spawns a fresh python3 PID;
|
|
195
|
+
# a PID-keyed session id would defeat the throttle).
|
|
196
|
+
COUNT_BEFORE=$(find "$APPS2" -path "*/sessions/*.json" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
197
|
+
PRESENCE_FILE=$(find "$APPS2" -path "*/sessions/*.json" -type f 2>/dev/null | head -1)
|
|
198
|
+
if [ -z "$PRESENCE_FILE" ]; then
|
|
199
|
+
fail "throttle setup" "no presence file from case 8"
|
|
200
|
+
else
|
|
201
|
+
MTIME_BEFORE=$(stat -f '%m' "$PRESENCE_FILE" 2>/dev/null || stat -c '%Y' "$PRESENCE_FILE" 2>/dev/null)
|
|
202
|
+
# Tiny sleep so any rewrite would change mtime (mtime resolution is 1s).
|
|
203
|
+
sleep 1
|
|
204
|
+
bash -c "cd '$NONREPO' && unset CLAUDE_PROJECT_DIR && \
|
|
205
|
+
printf '%s' '$EVENT_JSON' | \
|
|
206
|
+
BUILD_LOOP_APPS_ROOT='$APPS2' BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
207
|
+
BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=30 \
|
|
208
|
+
bash '$PE_HOOK'" </dev/null >/dev/null 2>&1
|
|
209
|
+
COUNT_AFTER=$(find "$APPS2" -path "*/sessions/*.json" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
210
|
+
MTIME_AFTER=$(stat -f '%m' "$PRESENCE_FILE" 2>/dev/null || stat -c '%Y' "$PRESENCE_FILE" 2>/dev/null)
|
|
211
|
+
if [ "$MTIME_BEFORE" = "$MTIME_AFTER" ] && [ "$COUNT_BEFORE" = "$COUNT_AFTER" ]; then
|
|
212
|
+
pass "pre-edit throttle: no double-write + no new file within window"
|
|
213
|
+
else
|
|
214
|
+
fail "pre-edit throttle" "mtime before=$MTIME_BEFORE after=$MTIME_AFTER; count before=$COUNT_BEFORE after=$COUNT_AFTER"
|
|
215
|
+
fi
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# Reset between cases.
|
|
219
|
+
rm -rf "$APPS2" && mkdir -p "$APPS2"
|
|
220
|
+
|
|
221
|
+
# ---- Case 10: Fail-open under env -i / minimal PATH. Each changed hook
|
|
222
|
+
# must exit 0 under the Claude Code minimal-PATH simulation, regardless of
|
|
223
|
+
# rally state.
|
|
224
|
+
for HOOK in "$SS_HOOK" "$PE_HOOK"; do
|
|
225
|
+
RC=$(env -i PATH=/usr/bin:/bin HOME=/tmp \
|
|
226
|
+
BUILD_LOOP_APPS_ROOT="$APPS2" \
|
|
227
|
+
BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
228
|
+
BUILD_LOOP_RALLY_POINT_SKIP_WATCH=1 \
|
|
229
|
+
bash "$HOOK" </dev/null >/dev/null 2>&1; echo $?)
|
|
230
|
+
if [ "$RC" -eq 0 ]; then
|
|
231
|
+
pass "env -i PATH=/usr/bin:/bin exit 0 ($(basename "$HOOK"))"
|
|
232
|
+
else
|
|
233
|
+
fail "minimal-PATH exit 0 ($(basename "$HOOK"))" "rc=$RC"
|
|
234
|
+
fi
|
|
235
|
+
done
|
|
236
|
+
|
|
237
|
+
# ---- Case 11: Fail-open when the rally_point package is missing. We
|
|
238
|
+
# point ``PKG`` (via shell-side directory absence check) at a stub root
|
|
239
|
+
# without scripts/rally_point — the hooks must still exit 0 cleanly.
|
|
240
|
+
STUB=$(mktemp -d)
|
|
241
|
+
mkdir -p "$STUB/hooks" "$STUB/scripts"
|
|
242
|
+
cp "$SS_HOOK" "$STUB/hooks/"
|
|
243
|
+
cp "$PE_HOOK" "$STUB/hooks/"
|
|
244
|
+
cp "$SCRIPT_DIR/_session_start_lib.sh" "$STUB/hooks/"
|
|
245
|
+
for HOOK_NAME in session-start-rally-point.sh pre-edit-rally-point.sh; do
|
|
246
|
+
RC=$(BUILD_LOOP_APPS_ROOT="$APPS2" BUILD_LOOP_RALLY_POINT_SKIP_WATCH=1 \
|
|
247
|
+
bash "$STUB/hooks/$HOOK_NAME" </dev/null >/dev/null 2>&1; echo $?)
|
|
248
|
+
if [ "$RC" -eq 0 ]; then
|
|
249
|
+
pass "fail-open exit 0 when rally_point package missing ($HOOK_NAME)"
|
|
250
|
+
else
|
|
251
|
+
fail "fail-open missing-package ($HOOK_NAME)" "rc=$RC"
|
|
252
|
+
fi
|
|
253
|
+
done
|
|
254
|
+
|
|
255
|
+
# =========================================================================
|
|
256
|
+
# New tests: status line, quiet flag, 64KB cap, socket hostname, cd guard
|
|
257
|
+
# =========================================================================
|
|
258
|
+
|
|
259
|
+
# ---- Case 12: visible status line "rally: joined <slug> room" prints on
|
|
260
|
+
# real join (throttle window set to 0 so the write always fires).
|
|
261
|
+
APPS3=$(mktemp -d)
|
|
262
|
+
REPO_Y=$(mktemp -d)
|
|
263
|
+
( cd "$REPO_Y" && git init -q && git config user.email t@e.com && git config user.name t )
|
|
264
|
+
EVENT_JSON_Y=$(printf '{"tool_input":{"file_path":"%s/z.py"}}' "$REPO_Y")
|
|
265
|
+
OUT=$(printf '%s' "$EVENT_JSON_Y" | \
|
|
266
|
+
BUILD_LOOP_APPS_ROOT="$APPS3" \
|
|
267
|
+
BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
268
|
+
BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=0 \
|
|
269
|
+
CLAUDE_PROJECT_DIR="$REPO_Y" \
|
|
270
|
+
bash "$PE_HOOK" 2>/dev/null)
|
|
271
|
+
RC=$?
|
|
272
|
+
if [ "$RC" -eq 0 ] && echo "$OUT" | grep -q "rally: joined"; then
|
|
273
|
+
pass "visible status: 'rally: joined <slug> room' prints on real join"
|
|
274
|
+
else
|
|
275
|
+
fail "visible status: join line missing" "rc=$RC out='$OUT'"
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
# ---- Case 13: status line is SILENT when throttled (write was skipped).
|
|
279
|
+
# Reuse APPS3 / REPO_Y from case 12; presence file is fresh, so the
|
|
280
|
+
# throttle (30s) blocks the write AND the status print.
|
|
281
|
+
OUT=$(printf '%s' "$EVENT_JSON_Y" | \
|
|
282
|
+
BUILD_LOOP_APPS_ROOT="$APPS3" \
|
|
283
|
+
BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
284
|
+
BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=30 \
|
|
285
|
+
CLAUDE_PROJECT_DIR="$REPO_Y" \
|
|
286
|
+
bash "$PE_HOOK" 2>/dev/null)
|
|
287
|
+
RC=$?
|
|
288
|
+
if [ "$RC" -eq 0 ] && ! echo "$OUT" | grep -q "rally: joined"; then
|
|
289
|
+
pass "visible status: silent when throttled (no double-print)"
|
|
290
|
+
else
|
|
291
|
+
fail "visible status: spurious join line when throttled" "rc=$RC out='$OUT'"
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
# ---- Case 14: BUILD_LOOP_RALLY_QUIET=1 suppresses the status line even
|
|
295
|
+
# on a real join (throttle=0 so a write would fire without the quiet flag).
|
|
296
|
+
APPS4=$(mktemp -d)
|
|
297
|
+
REPO_Z=$(mktemp -d)
|
|
298
|
+
( cd "$REPO_Z" && git init -q && git config user.email t@e.com && git config user.name t )
|
|
299
|
+
EVENT_JSON_Z=$(printf '{"tool_input":{"file_path":"%s/q.py"}}' "$REPO_Z")
|
|
300
|
+
OUT=$(printf '%s' "$EVENT_JSON_Z" | \
|
|
301
|
+
BUILD_LOOP_APPS_ROOT="$APPS4" \
|
|
302
|
+
BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
303
|
+
BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=0 \
|
|
304
|
+
BUILD_LOOP_RALLY_QUIET=1 \
|
|
305
|
+
CLAUDE_PROJECT_DIR="$REPO_Z" \
|
|
306
|
+
bash "$PE_HOOK" 2>/dev/null)
|
|
307
|
+
RC=$?
|
|
308
|
+
if [ "$RC" -eq 0 ] && [ -z "$OUT" ]; then
|
|
309
|
+
pass "BUILD_LOOP_RALLY_QUIET=1 suppresses join status line"
|
|
310
|
+
else
|
|
311
|
+
fail "QUIET flag did not suppress output" "rc=$RC out='$OUT'"
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
# ---- Case 15: 64 KB stdin cap — a payload larger than 64 KB must not
|
|
315
|
+
# cause a failure; the hook must exit 0 cleanly.
|
|
316
|
+
APPS5=$(mktemp -d)
|
|
317
|
+
REPO_W=$(mktemp -d)
|
|
318
|
+
( cd "$REPO_W" && git init -q && git config user.email t@e.com && git config user.name t )
|
|
319
|
+
# Build a ~100 KB JSON-ish blob (INVALID JSON so hooks.py falls through
|
|
320
|
+
# gracefully — the 64KB cap test is about not crashing, not about parsing).
|
|
321
|
+
BIG_PAYLOAD=$(python3 -c "print('{\"tool_input\":{\"file_path\":\"/tmp/x.py\",\"extra\":\"' + 'A'*102400 + '\"}}')")
|
|
322
|
+
RC=$(printf '%s' "$BIG_PAYLOAD" | \
|
|
323
|
+
BUILD_LOOP_APPS_ROOT="$APPS5" \
|
|
324
|
+
BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
325
|
+
BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=5 \
|
|
326
|
+
CLAUDE_PROJECT_DIR="$REPO_W" \
|
|
327
|
+
bash "$PE_HOOK" >/dev/null 2>/dev/null; echo $?)
|
|
328
|
+
if [ "$RC" -eq 0 ]; then
|
|
329
|
+
pass "64KB stdin cap: exit 0 on oversized payload"
|
|
330
|
+
else
|
|
331
|
+
fail "64KB stdin cap: hook errored on large stdin" "rc=$RC"
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
# ---- Case 16: socket.gethostname() — session_id must NOT end in "-host"
|
|
335
|
+
# (which was the fallback when HOSTNAME env var was missing). Under env -i
|
|
336
|
+
# the real hostname is always available via the syscall.
|
|
337
|
+
APPS6=$(mktemp -d)
|
|
338
|
+
REPO_H=$(mktemp -d)
|
|
339
|
+
( cd "$REPO_H" && git init -q && git config user.email t@e.com && git config user.name t )
|
|
340
|
+
EVENT_JSON_H=$(printf '{"tool_input":{"file_path":"%s/h.py"}}' "$REPO_H")
|
|
341
|
+
env -i PATH=/usr/bin:/bin HOME=/tmp \
|
|
342
|
+
BUILD_LOOP_APPS_ROOT="$APPS6" \
|
|
343
|
+
BUILD_LOOP_BRIDGE_INTERNAL_ONLY=1 \
|
|
344
|
+
BUILD_LOOP_RALLY_PRE_EDIT_THROTTLE_SECONDS=0 \
|
|
345
|
+
bash -c "printf '%s' '$EVENT_JSON_H' | CLAUDE_PROJECT_DIR='$REPO_H' bash '$PE_HOOK'" \
|
|
346
|
+
>/dev/null 2>/dev/null
|
|
347
|
+
PFILE=$(find "$APPS6" -path "*/sessions/*.json" -type f 2>/dev/null | head -1)
|
|
348
|
+
if [ -n "$PFILE" ]; then
|
|
349
|
+
SID=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print(d.get('session_id',''))" "$PFILE" 2>/dev/null || true)
|
|
350
|
+
if echo "$SID" | grep -qv "\-host$"; then
|
|
351
|
+
pass "socket.gethostname(): session_id does not end in '-host' under env -i"
|
|
352
|
+
else
|
|
353
|
+
fail "socket.gethostname(): session_id still ends in '-host'" "sid='$SID'"
|
|
354
|
+
fi
|
|
355
|
+
else
|
|
356
|
+
# No presence file written (env -i may lack python3 in /usr/bin path on this
|
|
357
|
+
# machine) — fall back to a direct Python unit check.
|
|
358
|
+
HOSTNAME_CHECK=$(env -i PATH=/usr/bin:/bin python3 -c "
|
|
359
|
+
import socket, sys
|
|
360
|
+
h = socket.gethostname()
|
|
361
|
+
sys.exit(0 if h and h != 'host' else 1)
|
|
362
|
+
" 2>/dev/null; echo $?)
|
|
363
|
+
if [ "$HOSTNAME_CHECK" = "0" ]; then
|
|
364
|
+
pass "socket.gethostname(): returns real hostname under env -i (direct check)"
|
|
365
|
+
else
|
|
366
|
+
fail "socket.gethostname(): returned empty or 'host' under env -i" ""
|
|
367
|
+
fi
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
# ---- Case 17: non-first-line `cd` must resolve to None (not the evil path).
|
|
371
|
+
# _extract_cd_target should return None for "echo x\ncd /evil".
|
|
372
|
+
RESULT=$(python3 -c "
|
|
373
|
+
import sys
|
|
374
|
+
sys.path.insert(0, '${REPO_ROOT}/scripts')
|
|
375
|
+
from rally_point.hooks import _extract_cd_target
|
|
376
|
+
r = _extract_cd_target('echo x\ncd /evil')
|
|
377
|
+
# .match() anchors at start; the leading 'echo' line must prevent the match.
|
|
378
|
+
assert r is None, f'Expected None, got {r!r}'
|
|
379
|
+
print('ok')
|
|
380
|
+
" 2>/dev/null)
|
|
381
|
+
if [ "$RESULT" = "ok" ]; then
|
|
382
|
+
pass "_extract_cd_target: non-first-line cd resolves to None (re.MULTILINE removed)"
|
|
383
|
+
else
|
|
384
|
+
fail "_extract_cd_target: non-first-line cd wrongly matched" "result='$RESULT'"
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
echo "Results: ${PASS} passed, ${FAIL} failed"
|
|
388
|
+
[ "$FAIL" -eq 0 ] || exit 1
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
# SEC-008 regression test for hooks/pre-commit and hooks/post-commit.
|
|
5
|
+
#
|
|
6
|
+
# Both hooks compute the repo toplevel via `git rev-parse --show-toplevel`.
|
|
7
|
+
# If that resolution fails, the old code produced an empty path, the
|
|
8
|
+
# `-f` file test silently failed, and the guard / capture was skipped —
|
|
9
|
+
# fail-open. The fix: `exit 2` when toplevel cannot be resolved.
|
|
10
|
+
#
|
|
11
|
+
# This test runs each hook from a directory that is NOT inside a git
|
|
12
|
+
# repo, so `git rev-parse --show-toplevel` yields nothing, and asserts
|
|
13
|
+
# the hook exits 2 instead of 0.
|
|
14
|
+
#
|
|
15
|
+
# Any failure exits non-zero.
|
|
16
|
+
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
PRE_HOOK="${SCRIPT_DIR}/pre-commit"
|
|
21
|
+
POST_HOOK="${SCRIPT_DIR}/post-commit"
|
|
22
|
+
|
|
23
|
+
PASS=0
|
|
24
|
+
FAIL=0
|
|
25
|
+
pass() { echo "PASS: $1"; PASS=$((PASS + 1)); }
|
|
26
|
+
fail() { echo "FAIL: $1 — $2"; FAIL=$((FAIL + 1)); }
|
|
27
|
+
|
|
28
|
+
# A directory guaranteed not to be inside a git repo.
|
|
29
|
+
NOREPO=$(mktemp -d)
|
|
30
|
+
trap 'rm -rf "$NOREPO"' EXIT
|
|
31
|
+
|
|
32
|
+
# ---- Case 1: pre-commit exits 2 when toplevel cannot be resolved ------
|
|
33
|
+
( cd "$NOREPO" && sh "$PRE_HOOK" </dev/null >/dev/null 2>&1 )
|
|
34
|
+
RC=$?
|
|
35
|
+
if [ "$RC" -eq 2 ]; then
|
|
36
|
+
pass "pre-commit fails closed (exit 2) with no resolvable toplevel"
|
|
37
|
+
else
|
|
38
|
+
fail "pre-commit no-toplevel" "expected exit 2, got $RC"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# ---- Case 2: post-commit exits 2 when toplevel cannot be resolved ----
|
|
42
|
+
( cd "$NOREPO" && sh "$POST_HOOK" </dev/null >/dev/null 2>&1 )
|
|
43
|
+
RC=$?
|
|
44
|
+
if [ "$RC" -eq 2 ]; then
|
|
45
|
+
pass "post-commit fails closed (exit 2) with no resolvable toplevel"
|
|
46
|
+
else
|
|
47
|
+
fail "post-commit no-toplevel" "expected exit 2, got $RC"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# ---- Case 3: pre-commit exits 0 inside a repo with no guard script ---
|
|
51
|
+
# (the .private-slug-check.py is not installed, so the guard segment
|
|
52
|
+
# finds no file and the hook proceeds to its trailing `exit 0`).
|
|
53
|
+
REPO=$(mktemp -d)
|
|
54
|
+
( cd "$REPO" && git init -q )
|
|
55
|
+
( cd "$REPO" && sh "$PRE_HOOK" </dev/null >/dev/null 2>&1 )
|
|
56
|
+
RC=$?
|
|
57
|
+
rm -rf "$REPO"
|
|
58
|
+
if [ "$RC" -eq 0 ]; then
|
|
59
|
+
pass "pre-commit exits 0 inside a repo when guard script absent"
|
|
60
|
+
else
|
|
61
|
+
fail "pre-commit in-repo-no-guard" "expected exit 0, got $RC"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
echo "----"
|
|
65
|
+
echo "PASS=$PASS FAIL=$FAIL"
|
|
66
|
+
[ "$FAIL" -eq 0 ]
|
package/package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tyroneross/build-loop",
|
|
3
|
+
"version": "0.30.3",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"registry": "https://npm.pkg.github.com",
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"description": "Multi-phase build orchestration with integrated debugging: assess, plan, execute, review, iterate.",
|
|
9
|
+
"main": "dist/src/index.js",
|
|
10
|
+
"types": "dist/src/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/src/index.d.ts",
|
|
14
|
+
"require": "./dist/src/index.js",
|
|
15
|
+
"import": "./dist/src/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"codex-skills",
|
|
21
|
+
"commands",
|
|
22
|
+
"skills",
|
|
23
|
+
"scripts/_paths.py",
|
|
24
|
+
"scripts/_db_url.py",
|
|
25
|
+
"scripts/blm.py",
|
|
26
|
+
"scripts/blm_api.py",
|
|
27
|
+
"scripts/check_cache_sync.py",
|
|
28
|
+
"scripts/embed_backend.py",
|
|
29
|
+
"scripts/install_memory.py",
|
|
30
|
+
"scripts/lessons_index",
|
|
31
|
+
"scripts/memory_context",
|
|
32
|
+
"scripts/memory_graph",
|
|
33
|
+
"scripts/project_resolver.py",
|
|
34
|
+
"scripts/semantic_index",
|
|
35
|
+
"scripts/sync_navgator_lessons.py",
|
|
36
|
+
"scripts/sync_plugin_cache.py",
|
|
37
|
+
"scripts/prune_plugin_cache.py",
|
|
38
|
+
"scripts/prune_codex_plugin_cache.py",
|
|
39
|
+
"hooks",
|
|
40
|
+
"agents",
|
|
41
|
+
"docs/agent-surface-policy.md",
|
|
42
|
+
"docs/memory-setup.md",
|
|
43
|
+
"templates/memory",
|
|
44
|
+
".cursor",
|
|
45
|
+
".claude-plugin",
|
|
46
|
+
".codex-plugin",
|
|
47
|
+
".agents",
|
|
48
|
+
"README.md",
|
|
49
|
+
"AGENTS.md",
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"!**/__pycache__",
|
|
52
|
+
"!**/__pycache__/**",
|
|
53
|
+
"!**/*.pyc"
|
|
54
|
+
],
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsc",
|
|
57
|
+
"watch": "tsc --watch",
|
|
58
|
+
"prepublishOnly": "npm run build",
|
|
59
|
+
"test": "python3 -m unittest scripts/test_commit_state_check.py scripts/test_bridge_preflight.py scripts/test_mcp_registration.py && bash scripts/hooks/test_hooks.sh",
|
|
60
|
+
"benchmark": "node benchmark-scoring.js",
|
|
61
|
+
"clean": "rm -rf dist",
|
|
62
|
+
"plugin:prune-cache": "python3 scripts/prune_plugin_cache.py --source .",
|
|
63
|
+
"plugin:prune-cache:apply": "python3 scripts/prune_plugin_cache.py --source . --apply",
|
|
64
|
+
"plugin:sync-cache": "python3 scripts/sync_plugin_cache.py --source . --host all",
|
|
65
|
+
"plugin:sync-cache:hooks": "python3 scripts/sync_plugin_cache.py --source . --host all --install-git-hooks",
|
|
66
|
+
"codex:prune-cache": "python3 scripts/prune_codex_plugin_cache.py --source .",
|
|
67
|
+
"codex:sync-cache": "python3 scripts/sync_plugin_cache.py --source . --host codex",
|
|
68
|
+
"codex:prune-cache:apply": "python3 scripts/prune_codex_plugin_cache.py --source . --apply",
|
|
69
|
+
"claude:prune-cache": "python3 scripts/prune_plugin_cache.py --source . --host claude",
|
|
70
|
+
"claude:sync-cache": "python3 scripts/sync_plugin_cache.py --source . --host claude",
|
|
71
|
+
"claude:prune-cache:apply": "python3 scripts/prune_plugin_cache.py --source . --host claude --apply"
|
|
72
|
+
},
|
|
73
|
+
"keywords": [
|
|
74
|
+
"build-loop",
|
|
75
|
+
"orchestration",
|
|
76
|
+
"codex",
|
|
77
|
+
"claude-code",
|
|
78
|
+
"plugin",
|
|
79
|
+
"agentic-development",
|
|
80
|
+
"spec-writing",
|
|
81
|
+
"evaluation"
|
|
82
|
+
],
|
|
83
|
+
"author": "Tyrone Ross",
|
|
84
|
+
"license": "Apache-2.0",
|
|
85
|
+
"repository": {
|
|
86
|
+
"type": "git",
|
|
87
|
+
"url": "git+https://github.com/tyroneross/build-loop.git"
|
|
88
|
+
},
|
|
89
|
+
"bugs": {
|
|
90
|
+
"url": "https://github.com/tyroneross/build-loop/issues"
|
|
91
|
+
},
|
|
92
|
+
"homepage": "https://github.com/tyroneross/build-loop#readme",
|
|
93
|
+
"dependencies": {
|
|
94
|
+
"commander": "^14.0.3",
|
|
95
|
+
"prompts": "^2.4.2"
|
|
96
|
+
},
|
|
97
|
+
"devDependencies": {
|
|
98
|
+
"@types/node": "^20.0.0",
|
|
99
|
+
"@types/prompts": "^2.4.9",
|
|
100
|
+
"typescript": "^5.0.0"
|
|
101
|
+
},
|
|
102
|
+
"engines": {
|
|
103
|
+
"node": ">=18.0.0"
|
|
104
|
+
},
|
|
105
|
+
"packageManager": "npm@10.x",
|
|
106
|
+
"os": [
|
|
107
|
+
"darwin",
|
|
108
|
+
"linux",
|
|
109
|
+
"win32"
|
|
110
|
+
]
|
|
111
|
+
}
|