Agentic development harness for Claude Code — SPEC-driven plan/run/sync, TRUST 5 quality gates, model+effort routing, and Claude×GLM multi-LLM cost control. Single Go binary, 16 languages, zero deps.
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
SPEC-TEAMMATE-REVIVAL-GUARD-001: stopped-teammate revival guard — stop registry + SendMessage deny (t267) (#1667) * feat(SPEC-TEAMMATE-REVIVAL-GUARD-001): plan-phase artifacts (Tier M, 3 artifacts) (t267) Mechanism layer beneath SPEC-TEAMMATE-REVIVAL-SOLE-WRITER-001 (doctrine, landed t269): stop-registry + PreToolUse SendMessage deny guard against revival of stopped teammates. Recommended design (plan.md §C): composition of fix direction 2 (reject-the-send) + 3 (audit visibility) over a per-session stop registry; direction 1 (runtime address reclamation) rejected as infeasible moai-side. Per-layer feasibility evidence E1-E8 measured on c9eed8ac6; live mid-session probe documented inconclusive (E7), live firing pinned as M1/M3 gate (E-P1). Open marker: [NEEDS CLARIFICATION: enforcement default] (plan.md §C.3). * fix(SPEC-TEAMMATE-REVIVAL-GUARD-001): plan-audit iteration-2 fix pass D1-D5 (t267) D1 [BLOCKING]: enforcement default RESOLVED false (operator decision 2026-08-26, orchestrator AskUserQuestion round, recommended option taken); clarification marker stripped from all surfaces (grep 0 hits); M3 default-flip upgrade trigger kept evidence-gated. D2: REQ-TRG-002 wiring corrected — unmatcher'd PostToolUse dispatch is handle-harness-observe.sh (moai hook harness-observe), evidence row B3. D3: full E-P1 recipe (steps 1-5) attributed to M2; M1 keeps steps 1-2; AC-TRG-010 -> M2 (stays P1); trim guard updated. D4: template twin path corrected to settings.json.tmpl (module field + AC-TRG-009; auditor-measured, re-verified by ls). D5: name [ref] recipient matching — REQ-TRG-003 suffix parse+strip; new AC-TRG-011 pins both directions (stopped [ref] denied, live [ref] allowed). D6-D9: not enumerated in dispatch; no verdict artifact in tree — left as documented debt pending enumeration (iteration 2 = Tier M last fix slot). spec.md v0.1.1; lint: No findings, exit 0. * docs(SPEC-TEAMMATE-REVIVAL-GUARD-001): plan-phase completion signal + R1/R2 mechanical follow-ups (t267) 1. progress.md §E.1: plan_status: audit-ready, plan_complete_at: 2026-08-26 — plan-audit iter-2 PASS-WITH-DEBT 0.88, report .moai/reports/plan-audit/SPEC-TEAMMATE-REVIVAL-GUARD-001-review-2.md 2. R1: AC-TRG-007 Given references the RESOLVED default (false) instead of stale 'C.3 recommendation' phrasing; value unchanged. 3. R2: §A.4 unit-AC range includes AC-TRG-011 (001-008 and 011). lint: No findings, exit 0. * docs(SPEC-TEAMMATE-REVIVAL-GUARD-001): §F Phase 4 mode selection — serial, autonomous progression (t267) 🗿 MoAI * feat(SPEC-TEAMMATE-REVIVAL-GUARD-001): M1 stop-registry observe+record layer (t267) PostToolUse(TaskStop) recorder riding the matcher-less harness-observe dispatch; per-session stop registry at .moai/state/agent-stops/<session>.json; JSONL audit at .moai/logs/agent-stop-audit.jsonl; PreToolUse(SendMessage) observer (advise-only) with name [ref] parse-and-strip; SendMessage|TaskStop PreToolUse matcher in settings.json + template twin (Template-First, make build). SPEC status draft -> in-progress on the M1 commit. 🗿 MoAI * feat(SPEC-TEAMMATE-REVIVAL-GUARD-001): M2 enforcement layer + config cache schema bump (t267) Deny path with STOPPED_TEAMMATE_VIOLATION sentinel (fires only on registry hit + opt-in gate); recipient matched against BOTH registry fields (name and agent_id — the live-measured TaskStop shape carries the spawn name in agent_id with name empty, pinned as a regression fixture); AC-TRG-011 name [ref] parse-and-strip both directions; spawn-name respawn-clear before the spawn proceeds (extractAgentSpawn gains a Name read); SessionEnd registry cleanup (session_cleared); workflow.agent_stop_guard config gate shipped default false + dev-repo local enable for dogfood. Config cache schema version 1 -> 2: a Config field ADDITION breaks backward compatibility with prior cache files — an old-binary cache omits the new key and the serving binary unmarshals a zero value over the live file value (observed: enabled:false served over an enabled:true workflow.yaml until MOAI_CONFIG_CACHE_DISABLED=1 proved the disk load correct). 🗿 MoAI * test(SPEC-TEAMMATE-REVIVAL-GUARD-001): M2 coverage closure — partial-removal + absent-noop pins (t267) removeAgentStopEntry sibling-keep write-back path and ClearAgentStops absent-registry no-op pinned; hook package coverage 85.1%. 🗿 MoAI * docs(SPEC-TEAMMATE-REVIVAL-GUARD-001): M3 deliverables — rule-amendment proposal, correlation recipe, §E.2/§E.3 (t267) proposal-rule-amendment.md: neutral mechanism-pointer amendment text for cross-session-messaging.md (sentinel meaning, kill switch, dev-repo local-enable note) + audit-to-commit-window correlation recipe with the measured worked example + recorded DO-NOT-FLIP verdict. progress.md §E.2: full AC-TRG-001..011 matrix with attribution triples (AC-TRG-010 PASS via the orchestrator's two live probes) + cross-cutting cache-defect note for CHANGELOG; §E.3: run audit-ready signal (run_commit_sha placeholder, backfill follows). 🗿 MoAI * docs(SPEC-TEAMMATE-REVIVAL-GUARD-001): backfill run_commit_sha aac630f25 (t267) 🗿 MoAI * docs(SPEC-TEAMMATE-REVIVAL-GUARD-001): M3 run-phase closure — §E.3 closure note + deliverable split (t267) Split the correlation recipe into its own file (audit-commit-correlation-recipe.md, 4-step procedure + measured 0-commit window example) per the requested sync-consumption surface; proposal-rule-amendment.md now carries the amendment text + flip verdict only. §E.3 gains the one-line closure note (M1/M2/M3 chain through 08c345cb4, DO-NOT-FLIP verdict, sustained dogfood recorded as the open trigger condition). §E.2/§E.3 substance was authored in aac630f25; this commit is surface compliance, not re-authoring. 🗿 MoAI * chore(SPEC-TEAMMATE-REVIVAL-GUARD-001): sync-phase artifacts, 3-phase close (t267) Single sync commit per the 3-phase close (plan -> run -> sync); the completed transition rides this commit (no separate Mx chore commit). - CHANGELOG.md [Unreleased]: two entries — the stop-guard feature (Added) and the pre-existing config disk-cache schema-poisoning fix (Fixed, own entry — it affects every future config-field addition, not just this SPEC). - Rules twins: apply proposal-rule-amendment.md §A.1 (neutral mechanism-layer note appended to the "Never address a stopped teammate by name" clause + "Reviving a stopped teammate" anti-pattern footnote) to BOTH the local rule and the template mirror; make build exit 0, catalog.yaml content unchanged. - Recipe placement: §B correlation recipe copied to .moai/docs/agent-stop-audit-correlation.md (local-only dev docs — no template mirror); SPEC-dir original (audit-commit-correlation-recipe.md, tracked in a1a908802) kept as-is. - spec.md frontmatter: status in-progress -> completed, updated 2026-08-27 (only frontmatter edit; body untouched). - progress.md §E.4 sync-phase audit-ready signal populated; sync_commit_sha placeholder backfilled in the immediately following commit. Always-loaded growth statement (rule-authoring duty b): this edit grows the always-loaded cross-session-messaging.md by ~1.3 KB (and its template twin identically). It amends an existing always-loaded HARD rule — the mechanism pointer is part of the prohibition it documents, so it cannot move to a paths-scoped companion without splitting the clause it qualifies. Sessions that never message a stopped teammate pay ~1.3 KB per turn and after every /clear; accepted because a STOPPED_TEAMMATE_VIOLATION deny must be interpretable wherever the rule loads, or it reads as a guard bug and gets routed around. 🗿 MoAI * docs(SPEC-TEAMMATE-REVIVAL-GUARD-001): backfill sync_commit_sha ea9b1f522 (t267) Complete the self-referential-hazard workaround (spec-frontmatter-schema.md D3): the §E.4 sync_commit_sha field now carries the real SHA of the sync commit ea9b1f522 that closed SPEC-TEAMMATE-REVIVAL-GUARD-001. 🗿 MoAI * docs(SPEC-TEAMMATE-REVIVAL-GUARD-001): §E.4 cites §E.3 run-chain closure note instead of restating (t267) Apply the lead's post-completion correction #2: the §E.4 close_statement no longer restates the M1-M3 commit chain (§E.3's run-phase closure note, landed in a1a908802, owns it) — it cites that note with its a1a908802 endpoint. Correction #1 needed no change: .moai/docs/agent-stop-audit-correlation.md was already sourced from the standalone audit-commit-correlation-recipe.md (body byte-identical; only the header placement line differs). 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 12 天前 | |
chore(local): normalise the dogfood ruleset, keep three orphans, ignore browser output (#1558) * chore(local): normalise the dogfood ruleset, keep three orphans, ignore browser output Three unrelated pieces of the same cleanup, all of it local-checkout only. Ruleset normalisation. The relocation in #1557 moved the ast-grep rules by `git mv` from HEAD, which carried the committed content and left behind the uncommitted edits sitting on top of it — Korean rule messages rewritten in English, rationale comments added, and sgconfig's ruleDirs prose reframed so a language absent from the list reads as an equal-priority future addition rather than an unsupported one. Those edits are restored here. Verified after restoring: `moai ast-grep --rules-dir .moai/astgrep-rules` loads the ruleset and reports findings. Three files that were only ever untracked. `.git_hooks/pre-commit` sits beside an already-tracked `pre-push`; `craft-fundamentals.md` and `hierarchical-scoring.md` sit beside already-tracked references in their own skills. Tracking them is what makes them recoverable — `moai update` deletes from these directories, and a tracked file comes back with `git restore` while an untracked one is simply gone. One caveat recorded rather than resolved: `hierarchical-scoring.md` overlaps `hrn-003-hierarchical-scoring.md`, which is tracked and has different content. Whether the new file supersedes the old is not mine to decide, so both are kept and the question is left visible. Browser output ignored. `e2e/screenshots/`, `e2e/web/`, `.playwright-mcp/` and stray root PNGs had accumulated 21 MB, all re-derivable by re-running the suite and all one `git add -A` away from an unintended commit. The patterns are deliberately narrow — `e2e/cli/*.sh` and curated fixtures stay tracked. `.moai/reports/` is deliberately NOT blanket-ignored: 48 files under it are already tracked, including HTML, so reports here are a curated corpus rather than build output. Widening the pattern would have contradicted that and would not have untracked anything anyway. * fix(local): point the ci-loop rule paths at the renamed skill directory The rename in #1557 left eight references behind. Two of them are load-bearing: the paths: frontmatter in ci-watch-protocol.md and ci-autofix-protocol.md still named .claude/skills/moai-workflow-ci-loop/ SKILL.md, a path that no longer exists, so neither rule could auto-load on the trigger it was written for. The SPEC bodies that mention the old name are deliberately left alone — they record what shipped at the time. --------- Co-authored-by: t <t@t.t> | 23 天前 | |
t250: graph freshness, symbol layer, and MCP code queries (SPEC-V3R6-GRAPH-FRESHNESS-001) (#1648) * docs(spec): author SPEC-V3R6-GRAPH-FRESHNESS-001 plan-phase artifacts (t250) Tier L (5 artifacts + progress). 20 REQ / 22 AC across M1-M5. Per-layer staleness metrics (codemaps git-diff-count / mx-index content-drift / edges source-fingerprint), mtime banned, absent first-class verdict, t241 mutant discipline incl. card mutants A/B, M4 additive layer, cache per-tree anchoring. Thresholds are calibration-pending defaults. * docs(spec): revise SPEC-V3R6-GRAPH-FRESHNESS-001 v1.1.0 per plan-audit iter-1 (t250) D1: CI job bootstraps mechanical layers (mx scan + graph build) before check — absent cannot fire in CI, signal scoped to codemaps drift; AC-GF-007 healthy-head-green clause; day-one posture = regenerate codemaps in the landing PR (threshold-raise-to-pass rejected as calibration-to-pass). Nice-1..5 folded: declarative AC-GF-013, dirty-gen fingerprint anchor, baseline artifact+method, 713/738 provenance fix, graft-report path footnote + 17 tracked recount. * docs(spec): fold iter-2 minor N1 — drift-count provenance causality fix (t250) The 713-vs-738 difference is a measured-ref difference (report-time origin/main vs review-time 294b4b6ab), not --since history drift. * feat(SPEC-V3R6-GRAPH-FRESHNESS-001): M1 per-layer drift gate with provenance stamping (t250) Provenance block (REQ-GF-003): shared mx.Provenance schema stamped into the mx sidecar (top-level field), edges.jsonl (.meta.json sidecar), and codemaps (provenance.json sidecar); tree root + commit-or-dirty anchor + per-layer source data (scan inventory / source fingerprints). Drift gate (REQ-GF-001/002/004): moai graph check reports each layer (codemaps endpoint-diff, mx-index inventory-diff, edges source-fingerprint-mismatch) numerically with verdicts fresh/stale/absent; absent is a distinct failing verdict; mtime is never a signal. Exit codes 0/1/2 via the exitCodeError vehicle. Gate + CI wiring (REQ-GF-005/006): moai gate step 0 (advisory default, blocking opt-in, notice on every posture) reading gate.yaml graph_freshness thresholds; standalone .github/workflows/graph-freshness.yml bootstraps mechanical layers then checks. MUTANT A killed with observed evidence: aged fixture exits 1 with the offending layer, value, and threshold named (go run ./cmd/moai graph check, exit 1 observed on a comparable stale stamp). 🗿 MoAI * feat(SPEC-V3R6-GRAPH-FRESHNESS-001): M2 query-time refresh, per-tree cache anchoring, update-cost budget (t250) Refresh engine (REQ-GF-007): mx.RefreshIndex re-parses only files whose content hash differs from the stamped inventory (changed-files-only), picks up new files, drops vanished tags, and re-stamps provenance; no LLM, no network. moai mx query refreshes a stale index before answering; moai graph query probes EdgesSourcesMoved + MXIndexNeedsRefresh and rebuilds the derived artifact when a source moved. Pre-provenance indexes keep the legacy answering contract (unjudgeable is not stale). Per-tree anchoring (REQ-GF-008): wrong-tree provenance forces a full rescan (never incremental trust across trees); every answer names its tree root and commit via the provenance footer; the cache substrate (sidecar inventory + meta fingerprints) is per-tree state. Budget (REQ-GF-009): measured on this repository — zero-change refresh 522ms, full scan 196ms over 2,597 inventoried files; default 2000ms budget keeps headroom over the measured ceiling and only warns, never blocks. MUTANT B killed with observed evidence: a stamp-only injection (drop freshly parsed tags) failed TestRefreshIndex_ReflectsUncommittedEdits ('refreshed index lost the b.go tag entirely') and the graph-side guard fails on an unchanged artifact after a source moved. 🗿 MoAI * feat(SPEC-V3R6-GRAPH-FRESHNESS-001): M3 content-addressed citation canon + mx hash anchoring (t250) Citation anchor (REQ-GF-010/012): graph.Citation carries excerpt + region content hash (normalized: line-trimmed, blank-dropped) with the file path for lookup; line numbers are convenience notation and NewCitation rejects line-only anchors; TreeSHA co-stamps the measured tree. ResolveCitation matches the region by content — two trees differing only by blank lines above the region resolve to each tree's true physical position (a line-anchored resolver returns the stale number and points at the wrong place), and an edited region resolves UNMATCHED with a reason (honest staleness, never force-resolution). mx-index anchoring (REQ-GF-011): every scanned tag carries ContentHash = sha256 of its own source line; AC-GF-013 pins that lines inserted above leave the hash identical while the convenience Line tracks the physical position after refresh. Stamp surface: 'moai graph stamp codemaps' records the provenance anchor at the end of a regeneration (content stays curated; only the anchor is mechanical). 🗿 MoAI * docs(SPEC-V3R6-GRAPH-FRESHNESS-001): day-one codemaps regeneration + provenance stamp + run evidence (t250) Codemaps regenerated for the current tree: dependencies.md gains the internal/graph node and its edges (cli->graph, hook->graph, graph->mx) plus the infra-package table row; data-flow.md gains section 7 (graph freshness flow). Both files pre-dated internal/graph entirely — the accumulated drift the gate exists to catch. Provenance stamped clean at the M3 commit via 'moai graph stamp codemaps' (provenance: tree=<worktree> commit=5b88fd0ab0d0); post-bootstrap moai graph check reports all three layers fresh on the real worktree. Threshold calibration recorded (137 described-source files over the last 10 commits, 233 over 50 — threshold 40 retained as ~2-3 commits of typical churn). Refresh-cost budget measured locally: zero-change 522ms, full scan 196ms over 2,597 files. progress.md section E.2/E.3 carry the full VCI-formatted evidence per AC (attribution triple per item); probe-function tests harden graph package coverage to 85.7%. 🗿 MoAI * feat(SPEC-V3R6-GRAPH-FRESHNESS-001): M4 code-derived edge layers + astx seam + grade matrix (t250) astx decoupled (REQ-GF-013): ExtractCalls extends the extractor with call/import/function-range captures (@code.call, @code.import, @code.caller + @func.node pairs) — seeded for go/python/js/ts/java/rust (name-based grade), honestly 'none' for the rest; scaffolded and CGO-off stays unsupported. The graph builder's extraction seam is the new internal/graph/symbol package: go list -deps on it carries no navigator-tier package (AC-GF-016, test-enforced). Additive layers (REQ-GF-014): BuildWithCodeLayers = doc Build + code layers; every doc edge survives with relationship fields unchanged (AC-GF-018 test). Real-tree build: 173,473 edges (code-call 161,092, code-import 12,112) with doc layers untouched (import 22, mx-spec 95, spec-depends 148). Refresh rides the M2 path (refreshEdgesArtifact now rebuilds with code layers; the mx-index inventory is the described-source fingerprint the code layer rides). Grade matrix (REQ-GF-016): 16/16 cells graded (6 name-based, 10 none); ValidateGradeMatrix reports a gradeless cell as a defect verdict naming the language; moai graph build prints any defect verdict. Disagreement (REQ-GF-015): doc-explicit import claims the code layer REFUTES are marked (doc is a curated summary — code-found/doc-silent is silence, not contradiction; module-path normalization via go.mod makes both layers speak local package paths). FIRST real instance observed on this tree, recorded not resolved: internal/lsp -> internal/astgrep (doc dotted claim; code scan of all internal/lsp finds no such import — astgrep's consumers are internal/cli and internal/hook). 🗿 MoAI * feat(SPEC-V3R6-GRAPH-FRESHNESS-001): M5 MCP code-query tools + baseline-first measurement (t250) graph_file_api (REQ-GF-017, first per plan): exported signatures only — the declaration line through the body-opening token, never the body; Go applies its capitalization export rule strictly. graph_find_code + graph_trace_calls (REQ-GF-018): callee/caller matches and caller/callee BFS over the code-call layer, every match carrying its resolution grade. Provenance (REQ-GF-019): every response names the tree root + commit (edges meta sidecar, fresh anchor fallback); the per-tree test pins that two trees with different content answer from their own artifacts (the t246 wrong-tree defect family, tested). Catalog moves 21 → 24 tools (registration/catalog guard green); moai-mcp-tools.md updated in both the local and template copies (neutral wording, no SPEC references). Baseline-first (AC-GF-020): .moai/reports/t250/m5-baseline.md recorded BEFORE this first M5 commit — real transcript-scan measurement (8 most recent sessions: Grep tool-use 0-1/session, Read 0-25/session) with the per-task gap stated honestly: no pre-M5 session performed the fixed task set, and a knowing-measurer simulation would fabricate counts, so the per-task baseline is recorded as an explicit gap per delegation instruction. 🗿 MoAI * feat(SPEC-V3R6-GRAPH-FRESHNESS-001): disagreement revival path + lead-ruling evidence + M4-M5 run evidence (t250) Revival path (lead ruling item 2): 'moai graph build --all-disagreements' re-marks the suppressed code-found/doc-silent direction with an explicit [revived] tag on code-import edges (BuildWithCodeLayersMode + DisagreementAll); default-mode markers and doc-edge preservation are unaffected (tested). Real-tree observation: 1,275 [revived] markers under the flag, 1 genuine marker in default mode — a decided-not-to-report signal stays retrievable. progress.md §E.2 carries the ruling's full compliance block: (1) the 1,505 composition (3 doc-side + 1,502 code-side suppressed; 378 code-side false positives removed by module-path normalization; 2 of 3 doc-side dissolved by the same), (3) the two doc-side false positives verified corroborated in code (internal/cli/profile_setup.go:16 and wizard/types.go:8 → pkg/models; internal/config/defaults.go → pkg/models), and (+1) the disagreement-instance line citation now co-stamped with its measured tree (dependencies.md:75 at 7261712f1 — the lead's :71 was the primary's pre-regeneration copy; the same M3 lesson live). M4-M5 §E evidence blocks and m5-post.md measurement record included (fixed task set resolved with 0 Grep / 0 Read tool-use events, 5 provenance-stamped tool calls; per-task baseline gap stated honestly in m5-baseline.md). 🗿 MoAI * test(SPEC-V3R6-GRAPH-FRESHNESS-001): module-normalization coverage for the symbol seam (t250) TestExtract_ModuleNormalization pins the go.mod module-prefix split: module-prefixed imports localize to repository-relative paths and carry Local=true; stdlib/external imports pass through non-local; partial prefixes never localize. This file was completed and measured (symbol package coverage 69.7% -> 88.2%) but missed the ruling-compliance commit's staging — a completed fix, not an unfinished edit. 🗿 MoAI * docs(SPEC-V3R6-GRAPH-FRESHNESS-001): sync-phase artifacts (t250) CHANGELOG [Unreleased]: 4 Added entries (graph check drift-gate family + gate step + CI job + graph stamp; code-derived edge layers; 3 MCP code-query tools; content-addressed citations) + 1 Changed entry (query-time refresh, per-tree anchoring, provenance on answers). Docs-site graph.md x4 locales: moai graph check / stamp sections + build/query behavior sentences (ko canonical -> en/ja/zh). spec.md frontmatter: in-progress -> implemented (completed rides the lead-side close after integration). progress.md: E.4 sync signal. Verify: go vet(graph,mx,cli)=0; moai spec lint=No findings; hugo --minify --gc=0 (4-locale sections rendered). 🗿 MoAI * fix(SPEC-V3R6-GRAPH-FRESHNESS-001): sync-audit MUST-FIX F1-F3 (t250) F1 path traversal (trust boundary): FileAPI now rejects any file path that does not resolve inside the project root — filepath.Rel containment check after Join, reject-not-clean; the relPath arrives from an LLM-facing MCP parameter. Regression test asserts ../../../etc-style escapes fail (TestFileAPI_RejectsPathEscape, PASS observed). F2 gofmt: check.go const block + mcp_code_tools.go map literal formatted; gofmt -l ./internal/graph/ now empty. NOTE: gofmt -l ./internal/cli/ still lists 28 PRE-EXISTING non-conforming files outside this SPEC's scope (baseline, untouched — repo-wide gofmt gate is a follow-up candidate, out of scope per the audit). F3 false ordering claim: progress.md §E.2 and m5-baseline.md reworded — the baseline was authored before the M5 implementation within the working session and committed TOGETHER (7f2e9e77d); same-commit pairs cannot prove ordering in git, so the claim now rests on the authoring session record. Gap statement unchanged. Also: moai-mcp-tools.md project_root section updated Six→Nine tools (both local and template copies) — the doc/server match guard (TestProjectRootDocMatchesServer) caught the omission in the full CLI suite; template copy follows the same edit. Verification: go test ./internal/graph/ ok (incl. F1 regression), go vet ./internal/graph/ ./internal/cli/ clean, doc-match + handler + build tests ok, gofmt -l graph empty. 🗿 MoAI * docs(report): preserve t250 sync-audit review 1 — PASS 85.2 (t250) * fix(SPEC-V3R6-GRAPH-FRESHNESS-001): CI PR #1648 findings — not-comparable exit 2, shallow fetch, count guard, i18n keys (t250) F4 graph-freshness job red (run 32775609689): two layers fixed. (a) graph-freshness.yml checkout now fetch-depth: 0 — a depth-1 checkout does not hold the stamped generation commit. (b) In the check, an UNRESOLVABLE stamped commit is no longer judged stale (a fabricated verdict from a diff that could not run): it is a distinct system error — exit 2, stderr naming the missing commit (the exit-code contract's system channel; the SPEC enum prescribes no not-comparable verdict, recorded as a spec-gap fix). Shallow-clone reproduction OBSERVED both ways: pre-fix binary reproduced the CI failure shape (verdict=stale, exit 1 on 'fatal: bad object'); fixed binary exits 2 with 'codemaps stamp 5b88fd0ab0d0 not comparable in this checkout: ... bad object ...' (verbatim in §E). Regression: engine + CLI tests assert the error and exit 2. F1 TestMoaiMCPTools_Count25 -> Count28: main's literal-pin design kept literal per instruction; registration/catalog equality guard remains the drift catcher. F2 TestConsumerOnly_M0AndMxByteUnchanged jurisdiction gate: AC-NS2-005a's consume-only contract binds Detect-layer change-sets; the guard now runs only when the branch diff touches internal/navigator/detect/ — an mx-package SPEC legitimately editing internal/mx no longer trips a guard with no standing over it, while Detect branches keep the full check. F3 web i18n key-set parity: the 3 new catalog tools required schema dictionary keys in all 4 locales (set-membership fix, not translation work); en/ko/ja/zh title+desc added. Verification unfiltered: mcp/graph/web ok (0.380s/4.844s/3.276s), hook ok (65.808s), named tests isolated green, go vet mcp/hook/web/graph clean. 🗿 MoAI * fix(graph): re-stamp codemaps provenance to a post-rebase commit (t250, SPEC-V3R6-GRAPH-FRESHNESS-001) The day-one stamp named 5b88fd0ab, a pre-rebase branch commit that no longer exists after the history rewrite — graph-freshness CI hit not-comparable (exit 2, run 32806650540). Re-anchored to f513013b5 (parent of this commit). Local CI-mirror (mx scan + graph build + check): all three layers fresh, exit 0. 🗿 MoAI * docs(report): preserve CR round-2 review comments — 69 findings (t250) * fix(SPEC-V3R6-GRAPH-FRESHNESS-001): CR round-2 P1 — boundary family + query accuracy + refresh hardening (t250) Boundary-check family (lead condition 1, FileAPI-F1 discipline applied): - ResolveCitation rejects citations whose File resolves outside root; all Resolution.Reasons are host-path-free (3855001919 lineage, 3855149319). - The three MCP code tools resolve their tree via resolveToolProjectRoot(req) with projectRootOption() registrations — a supplied project_root is honored, an invalid one REJECTED, no silent default-tree answer (3855001953). Observed wrong-tree regression: two trees with distinct content answer their own; invalid root errors (t261). - Codemaps provenance scope: DescribedRoots validated (empty/absolute/ escaping rejected ⇒ unjudgeable); TreeRoot deliberately NOT matched for the tracked codemaps artifact — one file replicates to every checkout, so a mismatched root is normal state, not a copied stamp (3855002115 + 3855149192, which also flags the committed dev-local tree_root; the checker no longer keys on it, and the field is documented informational for tracked artifacts). mx-index/edges (untracked) keep the hard match. - Regular-file guards at every walked read site (provenance fingerprint, refresh walk, symbol walk, FileAPI) — FIFO/socket/symlink entries are skipped, never opened (3855001937: an open would hang the check/refresh). - mx scan inventory keyed to the provenance root: ScanInventory(projectRoot) so '--path internal' partial scans resolve correctly at check time (3855001981); '..'-prefixed CHILD filenames stay inventoried — only real parent escapes excluded (3855002133). Query accuracy (codequery): - Extraction errors preserved with %w; unsupported/undreadable split (3855002024). Absent edges.jsonl maps to the actionable absent-layer error (3855002040). Caller dedupe key includes the target — one line invoking two symbols no longer drops the second (3855002033). Trace traversals index byTarget/byCaller once (3855002059). Depth bound named maxTraceDepth (3855002055). Go export rule via unicode.IsUpper (3855002067). bodyOpenIndex tracks bracket depth so signatures containing the delimiter inside types are not truncated (3855002078: Go map[string]struct{...}, Python annotations, Ruby kwargs). Refresh hardening: a transiently unreadable file carries its previous digest forward — not dropped as vanished (3855001935); unreadable directory subtrees are skipped, not fatal (3855001941); scanner errors reported via the new Scanner.RecordError API (3855001990). ScanFile checks the extension BEFORE reading (3855002126). 🗿 MoAI * test(SPEC-V3R6-GRAPH-FRESHNESS-001): CR round-2 P2 — test contracts + minor code fixes (t250) Test-contract strengthenings: - stamp codemaps command-level tests: valid provenance output, replacement of an existing (corrupted) stamp, no temp lingering after success (3855001919). - parseTag table asserts the ContentHash contract: non-empty on every row and pinned against the literal sha256 of one fully literal comment line — a regression to hashing parsed content or to an empty hash now fails; the scan-level insert-line-above case already lives in TestRefreshIndex_TagHashSurvivesLineDrift (3855149419). - catalog size as named invariant wantCatalogSize (3855002107). - graph check fixtures marshal mx.Provenance instead of hand-interpolated JSON — Windows temp paths' backslashes broke the string form (3855001906); dirty-stamp fixture moved onto the shared block writer. Minor code fixes: - WriteEdgesMeta records the real edge count (build + refresh callers pass len(edges)); the always-0 count read honest but was not; stale writeMetaMeta comment corrected (3855002085). - localizeModule marks the module ROOT import as local (module == modulePrefix), not just prefix-stripped children (3855149341). - RefreshIndex drops the dead newSum binding (3855001992 lineage). 🗿 MoAI * docs(SPEC-V3R6-GRAPH-FRESHNESS-001): CR round-2 P3 — honest claims + exact-set remeasurement (t250) - m5-post.md REMEASURED against the baseline's ORIGINAL task definitions (3855149200): the first post-run had substituted narrower targets (one file instead of the package, CodeEdges instead of the CLI seam). Exact set: T1 full internal/graph (18 files, 45 symbols), T2 CheckFreshness (17 matches), T3 refreshEdgesArtifact (callers 1 / callees 14), T4 grade constants (6+14), T5 full internal/mx (34 files, 81 symbols) — 28 tool calls, 0 Grep / 0 Read; call total now matches the table row-by-row (3855149207). - AC-GF-012 narrowed to PASS-WITH-DEBT and the CHANGELOG citation bullet scoped to the mechanical surfaces — the codemaps md bodies are LLM- written and adopt the canon at their next regeneration (3855001878). - Dead quoteJSON helper removed (fell out of the marshal-fixture conversion). 🗿 MoAI * fix(SPEC-V3R6-GRAPH-FRESHNESS-001): CR round-2 P4 — config-error exit 2, honest E.3, docs-site blocks (t250) - A PRESENT but malformed gate.yaml now exits 2 (3855149230): the shared loader logs-and-continues on parse errors by design, so graph check pre-validates the file itself — a broken config must not silently pass a tree its intended thresholds would fail. Regression observed: broken YAML fixture exits 2 with the config named (t261); absent config keeps the defaults path. - progress.md §E.3 restated honestly (3855149212): 20 pass / 2 pass-with- debt (AC-GF-012 codemap-writer deferral, AC-GF-022 bounded baseline) / 1 deferred evidence (AC-GF-007 pushed-branch CI red, push-forbidden in-run) — replacing the overstated 'all 22 pass'. - docs-site graph.md command blocks show output in all four locales, resolving the MD014 prompt-without-output warnings (3855149215; CLI output shown verbatim, identical across locales). 🗿 MoAI * fix(SPEC-V3R6-GRAPH-FRESHNESS-001): CR round-3 — 6 findings, one commit (t250) 1. m5-post remeasurement date corrected to 2026-08-25 (the rerun happened the same day; 08-26 was a stale clock reading). 2. progress.md post_taskset_tool_calls synchronized to 28, matching the exact-set m5-post table (AC-GF-022 evidence consistency). 3. mx scan REJECTS a --path resolving outside the project root — an external scan would persist tags the project-root-relative provenance inventory cannot cover. Observed red (t261): external-tree fixture errors with the containment reason and writes no sidecar (TestNewMxScanCmd_RejectsExternalScanPath PASS). 4. validateDescribedRoot resolves EXISTING described roots through EvalSymlinks before containment: filepath.Rel is lexical, so an in-tree symlink pointing outside would hand the fingerprint walk a path that reads past projectRoot. Absent roots stay absent contributors. Observed red (t261): symlinked root fixture reports the symlink-rejection verdict (TestCheckFreshness_SymlinkRootRejected PASS). 5. The absent-layer error names the repo-relative artifact only — the absolute project root no longer appears in an LLM-facing error. 6. [Major, perf] the refresh walk skips unsupported extensions BEFORE hashing — the extension rule ScanFile applies (comment-prefix lookup); hashing-then-dropping wasted a full read per non-source file on the interactive query-refresh hot path. Verification: unfiltered mcp/graph/web/hook all ok (0.371/5.162/3.499/ 29.882s), both new rejection tests isolated PASS, vet + golangci-lint clean on touched packages. 🗿 MoAI * fix(SPEC-V3R6-GRAPH-FRESHNESS-001): round-3 supplement — symlink containment + named exit codes (t250) PRIORITY 1 (symlink bypass Major): FileAPI and ResolveCitation now resolve BOTH the project root and the target through filepath.EvalSymlinks before containment (shared resolvedWithin helper) — filepath.Rel is a LEXICAL check, so an in-tree symlink pointing at an external file defeated the round-1 '..' block, the exact mirror escape. Both rejection observations (t261) recorded in TestFileAPI_RejectsSymlinkEscapeAndFIFO: (a) in-root symlink → external secret.go REJECTED (escapes the project root; the lexical check alone would have passed it) and the mirrored citation does not resolve; (b) a FIFO target is rejected as non-regular (mkfifo(1)-based; skips where unavailable) — the open would hang. Round-3's earlier check.go described-root fix already carries the same EvalSymlinks discipline; citation now also refuses non-regular targets before reading. Exit-code literals → named constants (exitStaleOrAbsent / exitSystemError; the fresh path returns nil per cobra convention) — the four bare sites in graph_check.go no longer drift from the documented 0/1/2 contract. Verification: unfiltered mcp/graph/web/hook all ok (0.156/5.174/2.888/ 30.835s); new observation test PASS; vet + GOOS=windows build + golangci-lint clean. 🗿 MoAI * fix(SPEC-V3R6-GRAPH-FRESHNESS-001): round-3 P1 hardening — resolved-path reads + 3 observed rejections (t250) Completes the lead's PRIORITY-1 prescription to the letter on top of 88cbea514 (which had landed the EvalSymlinks containment; the lead's grep predates that commit): - resolveWithin now RETURNS the resolved target; FileAPI and ResolveCitation Stat (os.Lstat) and read/extract only the RESOLVED path — the regular-file mode check runs on the file actually opened, not on a path os.Stat would have followed elsewhere. - Three observed rejections (t261), one per subtest: (1) in-root symlink → external secret.go rejected by FileAPI — the lexical check PASSES this input, the resolved check is what rejects it; (2) FIFO target rejected as non-regular; (3) ResolveCitation mirrors both: escaping symlink does not resolve; FIFO citation carries the non-regular reason. - Plus the MCP-level observation the lead asked for: TestGraphFileAPI_RejectsSymlinkEscape drives handleGraphFileAPI and observes the tool error (no external symbols leak through graph_file_api). Verification: unfiltered mcp/graph/web/hook ok (0.322/5.469/3.127/ 28.942s); all rejection subtests PASS; vet + GOOS=windows build + golangci-lint clean. 🗿 MoAI * docs(SPEC-V3R6-GRAPH-FRESHNESS-001): regenerate codemaps for current head + re-stamp (t250) The gate fired as designed on 0d15864ae (codemaps value=80 threshold=40 — two main merges moved 80 described-source files past the day-one regeneration). This is the day-one posture: content regenerated, then re-stamped. dependencies.md deltas (mechanical, measured from the tree): - New Infrastructure nodes: internal/graph/symbol (astx seam; go list -deps isolation verified), internal/codexwiring, internal/codexadapter, internal/ciwatch, internal/mcp (28-tool catalog), internal/chain. - New edges: cli → {codexwiring, ciwatch, mcp, chain}; graph → {symbol, navigator/tiers}; symbol → {mx, navigator/astx}; codexwiring → codexadapter → hook (measured import chain). - cli node count 109 → 188 non-test files; infra-package table gains seven rows (graph/symbol, codexwiring, codexadapter, ciwatch, mcp, chain, graph role expanded to MCP code queries). - import edges in the built artifact: 22 → 30 (the code layer derives them; the doc map now names the same packages). data-flow.md: adds section 8 (pre-commit hook preservation — the t230 three-way classifier, SHA-256 sidecar, backup-and-disclose flow). Re-stamped at 0d15864ae (clean, in-history). CI-mirror verification (build → mx scan --quiet → build → check): all three layers fresh, exit 0 — codemaps value=0 threshold=40, mx-index 0/1, edges 0/0. 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 13 天前 | |
docs(SPEC-CI-FLAKE-SERIES-001): sync-phase — implemented, observation window pending (t278) (#1670) * feat(SPEC-CI-FLAKE-SERIES-001): plan-phase artifacts (M, 4 artifacts) (t278) SPEC-CI-FLAKE-SERIES-001 v0.2.0 (card t278, absorbing t270/t271): spec.md (REQ 12, GEARS) + plan.md (M1-M4) + acceptance.md (AC 10) + progress.md (§E.1 audit-ready, §F mode: serial, semi-autonomous). Plan-audit iter-1: PASS-WITH-DEBT 0.86 (Tier M threshold 0.80), SHOULD-FIX D1/D2 + MINOR D3-D5 all resolved in v0.2.0. Implementation Kickoff Approval granted by operator 2026-08-26. Evidence: .moai/reports/t278/plan-audit-iter1.md. 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): M1 investigation artifacts (t278) Baseline sweep (attempt-aware, 537 runs 2026-08-10..26, 556 attempts): 4 current-defect flare-ups post-#1591 (p-hat 4/166); 2 pre-#1591 bilateral 2.72x occurrences found and excluded by timeline. Statistic decision: AND-gate adopted (per-round median AND ratio-of-medians over MaxUnits), fallback trimmed-median rejected on measured arithmetic. Artifacts: forensics.md, reproduction-rate.md, timing-statistic-decision.md, sweep-attempts.sh (v2), refetch-jobs.sh; progress.md E.2 evidence. 🗿 MoAI * fix(SPEC-CI-FLAKE-SERIES-001): M2 stop-rule re-poll closes poller TOCTOU (t278) TestConcurrentSendPoll's poller treated an empty-drain observation as terminal when sendersDone merely looked closed: the observation and the channel check are two instants, and the final Sends' pending renames can land between them (CI run 32774108273 a1: 97/100 loss, zero poll errors, 0.07s). REQ-CFS-001: a closed channel now triggers one re-poll started after all senders completed; only ITS 0/0 terminates, and its claims merge into the collected set. The stop rule is extracted into pollUntilDrained so the new TestPollerStopRule drives the same code path through a channel-handshake forced interleaving (97 sends -> 0/0 observation -> final 3 sends + close -> check). Observed on this tree: RED 97/100 pre-fix, GREEN 100/100 post-fix, mutant (old select-immediate-exit form restored) RED 97/100 again, restore GREEN. Carries the draft -> in-progress frontmatter transition (first run-phase code commit). 🗿 MoAI * fix(SPEC-CI-FLAKE-SERIES-001): M2 RT005 latency assertion rebuilt as 20-sample p95 (t278) The REQ-HAE-002 latency contract is p95 <= 100ms (AC-HAE-003), but TestConfigChange_RT005ReloadIntegration enforced a single-sample wall-clock maximum — a category error that turned one ~120ms scheduler preemption into a CI failure on a us-scale sync path (run 32815411885 a1: 123.195919ms, 0.15s test duration). REQ-CFS-007: the assertion now evaluates the contract's own statistic — nearest-rank p95 (19th of 20 = second-largest) over 20 fresh-handler samples, each sample's async goroutine drained via testutil.WaitForAsync before the next. One preempted sample cannot move the second-largest; two must land together. Mutant proof on this tree: time.Sleep(150ms) injected in the sync path -> RED (p95 151.163958ms over 20 samples); removed -> GREEN (-race -count=5 -run TestConfigChange, 5/5). 🗿 MoAI * fix(SPEC-CI-FLAKE-SERIES-001): M2 paired calibrated arm enforces AND-gate (t278) The calibrated arm of AssertPaired failed on the median of the per-round ratios alone. That estimator survives offset load steps but is inflated by noise phase-locked to the ref-first/fn-first alternation: half the rounds' ratios inflate while both medians stay level — CI observed a 2.47x per-round median against 1.09x medians on byte-identical code (run 32779472351 a1, the flake this SPEC exists to close). Per the M1 statistic decision (.moai/reports/t278/timing-statistic-decision.md): the paired path now computes BOTH figures and fails only when BOTH exceed MaxUnits (CheckRatioAnd). Each figure is individually robust to one noise class, so their agreement refuses each single-figure false positive while keeping bilateral regression detection — the pre-#1591 bilateral form (2.72x both figures) still fails, detection preserved. CheckRatio's pure-function signature and semantics are unchanged (the unpaired Assert/Check path and the existing pin tests paired_step_test.go:56/:79 compile and pass untouched); the p95/worst arms are factored into checkAbsolute shared by both gates. The paired log line now reports both figures for diagnosability. Property tests (paired_asym_test.go, synthetic distributions — no measurement, parallel-safe): (a) alternation-locked asymmetry, true ratio 1.00x — AND-gate passes AND a falsifier arm proves the per-round-alone estimator still trips on it (observed pre-fix on this tree: RED 'calibrated gate tripped ... 2.50x'); (b) homogeneous 3x — AND-gate still fails. Existing pins TestCalibratedRatioSurvivesOffsetLoadStep (load-step) and TestPairedRatioStillCatchesRealCostGrowth (4x) pass unchanged. All 3 AssertPaired caller sites green: paired_test.go:61 (1.00x/1.12x), observer_test.go:251 (0.99x/0.98x), pre_tool_branch_guard_integration_ test.go:207 (1.00x/1.01x). 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): M2 run-phase evidence in progress.md (t278) Per-fix RED/GREEN/mutant observations with verbatim outputs and tree SHAs, E-battery results on final tree 324883ebb, AC-CFS-010 marker-rule judgment (0 violations), and the M2 commit list. 🗿 MoAI * fix(SPEC-CI-FLAKE-SERIES-001): add Out of Scope subsection for spec-lint (t278) Restructure section 4's out-of-scope prose into a compliant '### 4.1 Out of Scope' subsection (6 list items) to clear the spec-lint MissingExclusions ERROR on PR #1666. Content unchanged; in-scope prose stays as the section intro. Version 0.2.1. Local verification: go run ./cmd/moai spec lint --strict -> exit 0, 0 findings for SPEC-CI-FLAKE-SERIES-001, 0 ERRORs repo-wide. 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): complete D2/AC-count residue from audit iter1 (t278) Finish two leftovers of the plan-audit iter1 fix pass (spec v0.2.0 claimed both applied; one edit had failed on an exact-match miss and the plan Tier table was not updated): - acceptance.md AC-CFS-007 matrix row: pin N = go_code=true workflow run count (matches REQ-CFS-010 and the section D.1 GREEN cell) - plan.md Tier table: AC count 9 -> 10 (AC-CFS-010 added by D1) 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): M3 merged 379b310a6 + M4 series analysis + window opened (t278) M3 closed: PR #1666 squash-merged (operator-approved), post-merge observation window opened at 2026-08-26T18:05:57Z, first run 32997835484. M4 artifacts: series-analysis.md (REQ-CFS-012 common factor finalized, reusable authoring rule named) + reproduction-rate.md §5 ledger opened. Companion cards t270/t271 closed as absorbed by this SPEC. 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): sync-phase artifacts — implemented, AC-CFS-007 window pending (t278) Sync-audit PASS-WITH-DEBT 0.942 (9 GREEN / 1 PENDING / 0 RED); the completed transition is deferred until the observation window closes (earliest 2026-09-02T18:05:57Z AND N>=40 go_code=true runs) — operator decision 2026-08-27. CHANGELOG entry, progress.md §E.4 signal, spec.md frontmatter in-progress -> implemented, and the sync-audit report. 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): backfill sync_commit_sha 3a7aaab37 (t278) 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): post-merge ledger first accrual N=2 (t278) Runs 32997835484 (Test/Race green before concurrency-cancel; valid observation) + 32999196269 (all green). 0 recurrences. 2/40 runs, 0/7 days. Run 33000104346 pending accrual. 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 12 天前 | |
docs(readme): carry v3.1.1 into all four locales, and fix five stale counts README.ko.md is canonical (operator decision, 2026-08-17); en, ja, and zh are derived from it in this same change, preserving the parity the four files depend on — 818 lines each, 12 H2 sections at identical line numbers, 65 H3. New: a "what v3.1.1 adds" subsection covering home hygiene (MOAI_HOME, moai doctor's Home Disk Usage check, moai clean --home), the cross-session messaging and prompt-cache config sections, statusline.forge, and a bare /loop running the kanban foreman. CLI table gains moai memory, moai tokens, and moai clean --home; the config section gains crosssession.yaml, cache.yaml, state.yaml, and statusline.yaml's forge key, plus a standalone note that gate.ast_grep_gate.rules_dir changed its DEFAULT and no longer has a code fallback. Five counts were wrong and are now measured, not asserted: ref skills 10→11, domain skills (5 in one place, 8 in another) →7, total CLI commands 36→49, settings tabs 10→11 in an image alt, and the config-sections sentence which read as if six were the whole set when 33 ship. The 12-agent catalog and the 11×3 profile grid are both correct and now say why: eleven MoAI-custom agents plus the built-in Explore, which inherits the session model and has no profile cell. Assets: two infographic sets in four locales each, and three hand-authored SVGs mirroring the README's mermaid blocks for the docs-site. The mermaid blocks stay — GitHub renders them natively — so each SVG header names README.ko.md as the source of truth to bound the drift that creates. 🗿 MoAI | 19 天前 | |
fix(cli): reject raw *exec.ExitError at the exit-code seam (t130) *exec.ExitError happens to satisfy the ExitCoder interface (ExitCode() int), so a %w-wrapped subprocess failure reaching cmd/moai made main adopt the subprocess's raw exit code (git's 128) and fang's moaiErrorHandler suppress the error box — the silent-failure pair from the t41 (moai worktree done) and t129 (moai hook worktree-create) recurrences, both previously patched at call sites only. Fix at the seam: internal/cli.ResolveExitCode refuses raw *exec.ExitError chain-wide, then matches intentional ExitCoder carriers (exitCodeError family, worktree.ExitCodeError). main.go and fang.go now resolve through it. Producer-side half: new internal/execerr.StatusDetail describes a subprocess failure (exit status + captured stderr) without chaining the raw type; 24 wrap sites across cli/spec/verify/github/worktree/hook/core switched from %w to it, matching the CommandError discipline in internal/core/git execGit. Guards (errors.As chain-based, not message-based): wrapped raw must map to exit 1 and render through moaiErrorHandler; intentional codes still resolve; raw-alongside-intentional is refused. Reproduction-first: both guards failed against the pre-fix seam. | 19 天前 | |
feat(t629): docs-site ignoreCommand — 문서 입력 경로 변경 시에만 재빌드 (#1688) moai-adk push 전량이 문서 사이트 재빌드(8/16~9/1 실측 500건·READY 499) — docs-site/vercel.json ignoreCommand 추가로 차단. mo.ai.kr www·bo 동일 패턴. 로컬 3케이스 실측: 진행=exit 1·스킵=exit 0·SHA부재=exit 1. dpl_2y9Qd3WnQ2 READY — 이 배포부터 활성. evidence: mo.ai.kr .moai/reports/t629/verdict.md | 7 天前 | |
docs(readme): restore 26 missing infographics, add GLM + Star History, prune orphan assets (#1239) The v3.0.2 README rewrite referenced 31 infographic paths that were never committed — a parallel session swept them away and they survived only inside that session's saved untracked-files commit. Every locale variant (-en/-ja/-zh) plus the DeepSWE benchmark chart rendered as a broken image on GitHub. Images: - Restore the 26 infographics the four READMEs actually reference, recovered verbatim from that commit and verified as valid PNG data. - Fold the no-locale `three-axes-infographic.png` / `why-harness-infographic.png` into their `-en` variants (git detected them as byte-identical renames). README (4-locale): - Drop the duplicated no-locale image references left behind by the v3.0.2 merge, so each figure appears once per locale instead of twice. - Fix two mislabeled cost-axis sub-headings whose titles did not match the content beneath them, and remove a stray orphan H3 above the self-improvement-axis H2. - Replace the TRUST 5 / 11-Agent paragraphs duplicated across two axis sections with the decision-memory and harness-self-evolution paragraphs. - Add a `Claude x GLM Multi-LLM` section (plan, three execution modes, per-tier model mapping) grounded in the docs-site multi-llm page, and a `Star History` section — both were dropped in earlier restructures. Repo hygiene: - Remove `docs/images/readme/**` (20 files) and `assets/images/readme/**` (4 files): zero references anywhere in the tree. - Remove the generated one-off reports under `reports/`. - Gitignore the local-only `main-fork/` and `.omp/` directories, which were polluting `git status` as untracked. 🗿 MoAI Co-authored-by: Goos Kim <manager-develop@mo.ai> | 1 个月前 | |
chore(e2e): track the TUX v3 CLI journey script, ignore its run artifacts (#1122) e2e/cli/tux3_journeys.sh is a self-contained, re-runnable J1-J6 CLI journey suite: it builds a fresh binary from the repo, runs every mutating journey inside a throwaway sandbox under $MOAI_E2E_SANDBOX (default /tmp/moai-e2e), and never touches the dev checkout. It is worth tracking. Its output is not. The script writes per-journey logs, results.tsv, matrix.tsv and hash dumps to $MOAI_E2E_RUN_DIR (default e2e/.runs/<suite>-<date>/), all regenerated on every run — so e2e/.runs/ is now gitignored. Without the rule a single local run leaves 20+ untracked artifacts that read as pending work. 🗿 MoAI Co-authored-by: Goos Kim <manager-develop@mo.ai> | 1 个月前 | |
fix(settings): stop GLM settings loss across console saves, launcher rewrites, and inert effort keys (#1676) * feat(web): absorb GLM Settings rename + tier effort select-lock WIP onto flash-default main Absorbs the working-tree GLM console changes from the previous session onto origin/main (3abde7053): section rename to "GLM Settings" across 4 locales, tier effort select max-lock client logic (app.js), and the glmkey / schemaform / handlers / i18n touches that carry them. The glm-5.3-flash model-constant pieces were superseded by upstream SPEC-GLM-FLASH-DEFAULT-001 (t289) and resolved in favor of upstream. Task: glm-settings-persist 🗿 MoAI * fix(cli): gate llm.yaml launcher rewrites on semantic change detection persistTeamMode rewrote .moai/config/sections/llm.yaml on every moai glm / cg / cc launch: the zero-seeded reload + typed whole-file re-marshal destroyed hand-written comments, flipped the file mode 0644->0600 (writeFileAtomic's perm), and touched mtime each launch, reopening a lost-update window against concurrent writers (RC1 of the GLM settings-persistence cluster). saveLLMSection now compares the desired post-fill section state with the persisted llm.yaml semantically (both unmarshaled to config.LLMConfig, reflect.DeepEqual, nil-vs-empty maps normalized) and skips the write when equal. Real changes — a team_mode transition or a first-launch default fill — still write through writeFileAtomic, and unrelated persisted state (per-tier glm.effort, context_windows) still survives the re-marshal. Task: glm-settings-persist 🗿 MoAI * fix(web): stop out-of-option-set select coercion on console saves selectedSchemaValue returned the raw persisted value with no guard that it exists in the rendered option set. An HTML <select> whose current value matches no <option> auto-selects the first option and selects always submit on POST, so any console save from any tab silently rewrote an unknown value to the first offered option — e.g. a GLM tier slot still holding glm-5.2, which ValidGLMModels no longer offers (RC2 of the GLM settings-persistence cluster). Two halves: - Render: schemaSelectRow appends one synthetic option carrying the exact persisted value (label "<value> (saved)") and marks it selected when the value is outside the offered set, so the submission round-trips the value unchanged. Radios are untouched — an unlisted radio leaves every button unchecked and submits nothing. - Validation: parseSchemaForm gains the disk current values and accepts a submitted value that EQUALS the previously persisted value even outside the closed set (passthrough-preserve); genuinely new out-of-set submissions are still rejected atomically. Task: glm-settings-persist 🗿 MoAI * feat(cli): apply stored glm.effort[slot] to the GLM main-session launch The four llm.glm.effort.* keys were write-only: ApplySchemaEdits stored them, GLMConfigFromYAML structurally dropped Effort, no runtime path read them, and the console labeled them stored-only (RC3 of the GLM settings-persistence cluster). Wiring: - GLMConfigFromYAML carries config.GLMTierEffort through loadGLMConfig; resolveGLMBackendForLaunch hands the persisted map to launchClaude. - glmSlotEffortForModel resolves the slot serving the main-session model via the SAME alias/slot pairing setGLMEnv uses for ANTHROPIC_DEFAULT_*_MODEL (opus→high, sonnet→medium, haiku→low, fable→fable; [1m] split first, canonical ids reverse-mapped through ModelAliasFromCanonicalID). - resolveGLMMainSessionEffort: a non-empty stored slot value overrides the prefs.EffortLevel → MapModelPolicyToEffort chain; empty stored value keeps the chain byte-identical to before. The collapse overlay stays governing for the wire value — stored high and max both reach z.ai as reasoning_effort=max, low wires as low, and glm-5.3-flash pins every effort to max. The stored-only labels the console pinned are now false, so they are updated: schema StoreOnly marking removed, the panel honesty note and per-tier field descriptions rewritten in all four locales, and the types.go / sectionapply.go store-only comments corrected. Task: glm-settings-persist 🗿 MoAI * chore(web): drop unused submitSchemaForm test helper golangci-lint (unused) flagged the helper; the round-trip test reuses postGLMSave from glmkey_test.go instead. Task: glm-settings-persist 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 12 天前 | |
feat(SPEC-LSEL-LOCAL-EVOLUTION-001): GOOS-local self-evolution loop — drain/propose/apply-bypass/verify/reflection (M1-M4) (#1346) * feat(SPEC-LSEL-LOCAL-EVOLUTION-001): M1 drain closure — hns-lsel-curator + first 629-stub backlog drain M1 of the Local Self-Evolution Loop: closes the DRAIN gap (the cheapest seam — NO APPLY yet; observe/cluster/remember only). The constitution named the orchestrator as drain actor but until now drain existed only as a doctrine paragraph with zero mechanical code (moai-constitution.md:147). Deliverables (all user-owned surfaces; doctrine-zero-touch verified): - .claude/skills/hns-lsel-curator/SKILL.md — CLUSTER + drain engine skill (hns- namespace, NOT templated; English body). - .claude/skills/hns-lsel-curator/drain.sh — mechanical drain engine (bash + jq): companion-offset drain of lessons-inbox.jsonl; drain-side severity filter drops the ~65% Bash-timeout/sandbox noise (Bash:UnknownFailure + Bash:SandboxViolation + *:TimeoutError) BEFORE clustering; event_key clustering with a freq>=2 singleton gate; Generative-Agents-style 1-10 importance score. The filter is drain-side because internal/hook/failure_observer.go is OUTSIDE the six loop-writable surfaces. - .claude/skills/hns-lsel-curator/drain_test.sh — TDD RED→GREEN fixture characterization test (7 assertions: offset advance, noise exclusion pre-cluster, singleton discard, frequencies, 1-10 importance, zero memory/ writes, idempotent re-drain no-op). - .moai/state/lsel/{drain-offset.json,clusters.json} — gitignored state (advanced 0→629; 16 candidate clusters staged; ZERO memory/ writes — M1 produces candidates only). First real drain of the backlog: 629 stubs (live re-measure; was 569 at report time — moving target per AC-LSEL-009) → 446 noise + 3 singletons discarded → 16 candidates. Top: Agent:UnknownFailure(41), Read:UnknownFailure(35), Bash:ExitError(20). AC-LSEL-009 PASS: offset 0→629; 16 candidates; find memory -newer <drain-start> -name feedback_* → 0. AC-LSEL-010 PASS: tool_failure:Bash:* share of accepted = 3/16 = 18.8% (< 30%); zero noise keys leak into candidates. Invariants held: REQ-LSEL-003 (enableTriggerInjectionWrites stays false at applier.go:22 — bypass, never unfreeze); REQ-LSEL-006 (split_namespace_test PASS; zero lsel/hns-lsel leak into internal/template/templates/); AP-LSEL-006 (M1 is drain-only, NOT "loop closed" — APPLY lands in M3). This commit carries the draft→in-progress transition (spec.md frontmatter status flip). Plan-phase artifacts were previously uncommitted; they land together with M1 here. PR-mandatory per repo-local-pr-policy.md — no push to main from this commit. 🗿 MoAI * feat(SPEC-LSEL-LOCAL-EVOLUTION-001): M2 PROPOSE shadow + INVARANTS kernel M2 closes the PROPOSE half of the LSEL loop in user-owned surfaces. AC-LSEL-005 (CSA forced-gate): curator skill enumerates all 6 CSA categories (INVARANTS kernel, security/validation exception, HIGH-fan-in, Bash risk path, permissions.allow, execution-meta) + bother-cost-exemption clause; csa_refusal_test confirms fixture WITHOUT marker REFUSED (+ reject-log row) and WITH marker proceeds. AC-LSEL-006 (namespace + leak guard): extended internal_content_leak_test.go with 3 LSEL leak classes (L1 SPEC-LSEL-*, L2 hns-lsel-*, L3 CLAUDE.local.md internal marker) + positive-control fixture; named CI workflow lsel-leak-guard.yaml; zero lsel/hns-lsel/SPEC-LSEL content under templates/ (negative control clean). AC-LSEL-007 (mechanical trigger): backlog_check.sh SessionStart advisory + default /loop recipe lsel-drain-loop.js (read-only, cadence-bridge compliant). AC-LSEL-011 (PROPOSE shadow): sample proposal lsel-001 carries full 8-key schema + retrieval_evidence + diff.patch + self-critique with UNRESOLVED objection (blocked). propose_test validates schema + retrieval + gate-fire. AC-LSEL-012 (Tier-4 firing verification): tier4_firing_test verified the moai-harness-learner Tier-4 AskUserQuestion flow is DEAD at the production invocation layer (CuratorDispatch 0 callers; enableTriggerInjectionWrites=false; CLI prints stub string; no mechanical trigger). Per acceptance.md §E edge case, M2 downgrades to PROPOSE shadow only; APPROVE routes via M3 fresh path (hns-lsel-applier + decision.json). M2 wiring does NOT depend on the Tier-4 flow. CLAUDE.local.md: §0 INVARANTS kernel (read-only goal, top of file) + §28 LSEL operating instructions (~40 local lines, ko/en mix). Zero CLAUDE.md doctrine change. TDD RED→GREEN: propose_test, csa_refusal_test, leak-test positive control each captured RED then GREEN. Build + lint + full template suite green; coverage 85.9%. 🗿 MoAI * fix(SPEC-LSEL-LOCAL-EVOLUTION-001): M2 tier4_firing_test set -e/pipefail abort in DEAD-state probe 1/4 grep|wc pipelines exited 1 under set -euo pipefail when the filter chain produced zero lines (the expected DEAD/PASS state), aborting the script before the characterization PASS message. Add || var=0 guards so the count is preserved and the script reaches the finding block + exit 0. Caught by orchestrator trust-but-verify (manager-develop's 'characterization PASS' self-report did not observe the actual exit code). 🗿 MoAI * revert(SPEC-LSEL-LOCAL-EVOLUTION-001): drop incomplete tier4_firing_test fix The prior fix (2de244021) stopped the probe-1/4 pipefail abort so the script reaches the finding block, but did NOT achieve exit 0: probe 4 still FAILs because its grep over-matches non-invocation AskUserQuestion references (comments/strings) in harness Go. The commit message's 'exit 0' claim was therefore inaccurate. Reverting to the M2-shipped state; the full tier4_firing_test.sh fix (pipefail guard + probe-4 filter narrowing) is M2 craft debt for a proper manager-develop follow-up. AC-LSEL-012 substantive goal (Tier-4 DEAD, independently confirmed) is unaffected. 🗿 MoAI * feat(SPEC-LSEL-LOCAL-EVOLUTION-001): M3 APPLY bypass — applier + frozen allowlist + playback hook + rollback rehearsal M3 closes the LSEL loop's PROPOSE→APPLY seam via a parallel user-owned applier that BYPASSES the frozen Go applier (internal/harness/applier.go:22 write-flag stays false — REQ-LSEL-003). Deliverables: - .claude/lsel/frozen-allowlist.json (NEW, outside the 6 evolvable surfaces): frozen_patterns regex + execution_meta categories (REQ-LSEL-001/002) - .moai/hooks/lsel-apply.sh (NEW): playback-only consumer of approved decision.json (frozen-reject → exec-meta forced-gate → git apply → ledger → lsel-* commit). No new-apply/self-approve primitive (REQ-LSEL-008) - .claude/skills/hns-lsel-applier/{SKILL.md,apply_test.sh, rollback_rehearsal_test.sh}: APPLY engine skill + AC-001/002/005/008/013 characterization + AC-014 SHIP-GATE rollback rehearsal (all GREEN) - .claude/skills/hns-lsel-curator/{SKILL.md,tier4_firing_test.sh} (EDIT): AC-003 literal-token scrub + tier4 pipefail-abort fix (M2 debt) REQ-LSEL-003 preserved: frozen-flag identifier = 0 literal matches across all LSEL surfaces; applier.go:22 unchanged. Subagent boundary: 0 AskUserQuestion matches in hns-lsel-applier. go test ./... green; lint 0. 🗿 MoAI * feat(lsel-lsel-m3-smoke): LSEL apply — memory/feedback_lsel_m3_smoke.md lsel-lsel-m3-smoke Playback via lsel-apply.sh (decision.json + diff.patch). 🗿 MoAI * feat(SPEC-LSEL-LOCAL-EVOLUTION-001): M4 VERIFY (/moai gate superset + retry/auto-revert) + reflection (synthesis + archive + decay retrieval) M4 hardens the closed LSEL loop with two MUST ACs: AC-LSEL-015 (VERIFY independence + flaky-retry, REQ-LSEL-013): - verify.sh: mechanical post-apply VERIFY core. Runs the proposal's verify_command with a 2-attempt timeout-retry-once policy; on a second non-timeout failure auto-fires git revert lsel-id + marks the proposal's feedback_*.md verified:false; appends a verified ledger row. - The /moai gate (lint+format+type+test) superset is MANDATORY, not optional — proposer-authored verify alone is circular (report §11 mustFix B#6 / AP-LSEL-004). A bash hook cannot invoke the slash command, so the gate runs MODEL-SIDE (documented + grep-proven in the applier SKILL). - verify_test.sh: hermetic temp-repo characterization (pass / timeout-retry- pass / fail-twice-revert + /moai gate MANDATORY grep). AC-LSEL-016 (REFLECTION consolidation + decay-weighted retrieval, REQ-LSEL-014): - reflect.sh: mechanical periodic REFLECTION core. Threshold-fired (sum importance ~150, NOT wall-clock) — given 3+ concrete topic files above the threshold, synthesizes ONE abstract-principle feedback_*.md; originals MOVED to memory/_archive/ (cold tier, NEVER deleted — report §10 P4 archive-not-delete); principle carries a memory_type label (CoALA). - Decay-weighted retrieval: originals relocate to _archive/, so the active recall set holds the principle, ranked above the archived originals. - reflect_test.sh: hermetic temp-memory characterization (synthesis + archive-not-delete + memory_type + decay retrieval probe; below-threshold no-op edge case). E4 broadened from M3's applier-only scope to the full hns-lsel-* glob; surfaced 5 M2-shipped documentation literals in the curator, resolved by applying the applier's established token discipline (text-only, no doctrine change). REQ-LSEL-003 (frozen flag stays false) + REQ-LSEL-008 (lsel-apply.sh stays playback-only) invariants preserved. M3 regression guard (apply_test + rollback_rehearsal_test) stays green. 🗿 MoAI * docs(SPEC-LSEL-LOCAL-EVOLUTION-001): sync-phase artifacts (3-phase close) Sync-phase close for the GOOS-local LSEL dogfood mechanism: frontmatter in-progress → implemented → completed on spec.md (sole YAML-frontmatter artifact), progress.md §E.3 run_commit_sha backfilled to terminal run-phase commit 9ede1bfad, progress.md §E.4 sync-phase signal populated, and a single CHANGELOG [Unreleased] entry. LSEL closes the audit's central finding (frozen Go applier enableTriggerInjectionWrites=false, 0 production callers) via a parallel user-owned bypass applier writing only to the 6 evolvable surfaces; distributed templates, rules, retained agents, and the frozen Go applier stay byte-for-byte untouched. 16/16 MUST AC PASS; M5 (personalization) deferred-conditional (no MUST AC; pending a named simulation-harness SPEC). sync_commit_sha left as pending-backfill-* placeholder in this commit (self-referential-hazard workaround per spec-frontmatter-schema.md D3); backfilled in a follow-up commit after this sync PR merges. Route B (PR-mandatory, enforce_admins:true repo-local override). 🗿 MoAI * fix(SPEC-LSEL-LOCAL-EVOLUTION-001): close sync-auditor F1 — validate diff.patch paths (not just target_surface) + F2 counter F1 (BLOCKING security defect): lsel-apply.sh validated only the declared $TARGET (from decision.json target_surface) against the frozen allowlist + execution-meta list, but never validated the patch's own embedded destination paths (the +++ b/<path> lines git apply actually writes). A divergent proposal declaring an evolvable target_surface could write a FROZEN path via diff.patch with exit 0 and no reject-log row — the central safety claim of REQ-LSEL-001 ('confine every write to the 6 evolvable surfaces') was unmet. Additionally $TARGET was matched with a non-anchored re.search and not normalized, so memory/../internal/template/templates/x slipped past the anchored ^internal/template/templates/ pattern. Fix: 1. Normalize $TARGET via os.path.normpath before matching (resolves '..' traversal + relative segments). 2. Switch frozen_match from re.search to re.match (anchored). 3. Add a PRE-APPLY patch-path validation gate: extract every destination path from BOTH the diff --git a/X b/Y headers AND the +++ b/Y lines, normalize each, and run it through the SAME frozen_match + exec_meta_match. Any frozen patch path -> hard-reject (exit 2, reject-log row naming category=frozen-path + the offending path) BEFORE git apply, so NO write occurs on refusal. The gate is wrapped in set +e/set -e so a non-zero verdict is captured into GATE_RC without aborting the script before the reject-log is written (set -e would otherwise abort on the command substitution and leave no audit trail). 4. Scope-consistency guard (SHOULD, defense-in-depth): warn on stderr if a normalized patch path is outside the declared target_surface subtree. The prior 'status: completed' transition was premature pending this fix — the PR is HELD until this lands + re-verifies + re-audits PASS. F2 (consistency): progress.md §E.3 ac_pass_count: 9 contradicted must_ac_pass_count: 16 and the 16/16 MUST-AC claim. Corrected to 16. Verified: - apply_test.sh: all PASS including new divergent-patch + traversal cases - csa_refusal/apply/rollback_rehearsal/verify/reflect/propose/backlog_check: PASS - E1 verbatim F1 reproduction: REFUSED, frozen target unmodified, reject-log row, no write - go test ./... exit 0; go test ./internal/spec/... exit 0 - applier.go:22 enableTriggerInjectionWrites=false (REQ-LSEL-003 untouched) - lsel-apply.sh: 0 forbidden primitives (REQ-LSEL-008 playback-only preserved) 🗿 MoAI * fix(template): regenerate stale sync-auditor catalog hash (pre-existing main drift) origin/main catalog.yaml hash for sync-auditor did not match its .md (4ed068ce vs 13414211), introduced when SPEC-SYNC-AUDIT-FALSIFICATION-001 (#1344) edited the agent without refreshing the catalog. TestCatalogHashParity/TestManifestHashFormat fail on origin/main itself. 1-line deterministic regen to unblock the LSEL PR CI; not LSEL work, folded here to avoid a separate hotfix cycle. 🗿 MoAI --------- Co-authored-by: Goos Kim <manager-develop@mo.ai> | 1 个月前 | |
chore: bump version to v3.1.3 Version stamps across pkg/version/version.go, the four README release badges, and .moai/config/sections/system.yaml (moai.version and moai.template_version). | 15 天前 | |
fix(release): stop publishing the raw commit list as the release body (#1590) * fix(release): stop publishing the raw commit list as the release body v3.1.1 shipped with a 49,000-character body of commit subjects. GoReleaser's changelog.use=github built it, and the phase meant to overwrite it never ran — it would have blocked on a Korean notes file nobody had authored, so the English notes were hostage to a translation step that was not happening. Three changes, one path: - .goreleaser.yml sets changelog.disable:true. The release now arrives with an empty body rather than a competing one. - Phase 7 of the release harness fills that body via `gh release edit --notes-file`, and is rewritten from extraction to composition. Neither source is safe to paste: the commit list buries what changed, and the CHANGELOG section is an internal record of SPEC ids, ACs, and coverage figures. The notes are written from the CHANGELOG's `### Summary`, which Phase 4 now carries an explicit obligation to keep complete. - The per-version Korean notes file is retired. release.sh no longer appends it, and the four docs-site changelog pages no longer claim GitHub Releases carries a Korean body. The existing v3.1.0.ko.md stays — the retirement is forward-looking. The docs-site pages are also restated as pointers rather than mirrors, so the per-release obligation there is a check that they still describe reality, not a copy into four locales that drifts silently. 🗿 MoAI * fix(release): watch this release's run, and stop waiting eventually Two review findings on the release-notes path, both real. The GoReleaser wait took the newest release.yml run rather than the one for the tag just pushed, so a re-run of an earlier tag or a concurrent release would be watched instead and its verdict reported as this release's. The lookup now resolves the tag to its commit and filters on it, and polls for the run rather than reading once five seconds after the push. The wait itself had no deadline: `gh run watch` blocks until the run ends, so a hung workflow held the release script open indefinitely. It is now a poll with a 45-minute ceiling — past the observed build time, short enough to surface a stall — after which the script detaches and says how to resume watching. Release Drafter's template still told maintainers to write CHANGELOG.md in English and Korean, which is the convention this path retired. It now states the English-only rule and names the `### Summary` section the harness composes the published notes from. 🗿 MoAI * docs(release-drafter): fix the dropped relative pronoun The draft notice read "This is a draft Release Drafter refreshes on every PR merge", which parses as a noun pile rather than a sentence. 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 18 天前 | |
test(integration): align IT04 frozen-guard expectation with brand-dir removal The .moai/project/brand/ prefix was removed from the FROZEN set (SPEC-BRAND-DIR-REMOVE-001); the integration test still asserted IsFrozen==true for brand-voice.md, failing on all 3 OS. Move the path to the non-frozen list to match the unit-test ground truth. 🗿 MoAI | 1 个月前 | |
feat(SPEC-REF-SEO-ABSORB-001): add moai-ref-seo reference skill with full registration surface (#1274) * feat(SPEC-REF-SEO-ABSORB-001): plan-phase artifacts (Tier M, 5 artifacts) moai-ref-seo 레퍼런스 스킬 클린룸 흡수 + 재사용 가능한 클린룸 프로토콜 확립. Epic 1/3 — 형제 흡수 SPEC 2건이 본 SPEC의 클린룸 프로토콜을 재사용한다. plan-auditor 2회 감사(0.72 FAIL -> 0.79 FAIL, must-pass 7/7)를 거쳐 MUST-FIX 9건 정정 + 미해소 결정 5건 확정. iteration 3 감사 대기. 공유 체크아웃에 동시 세션이 관측되어 untracked 유실을 막기 위한 보호 커밋. 🗿 MoAI * docs(SPEC-REF-SEO-ABSORB-001): plan-audit iteration 3 PASS + N10 close (v0.3.1) plan-auditor iteration 3: PASS 0.87 (threshold 0.80), must-pass 7/7, regression 0. Score trend 0.72 -> 0.79 -> 0.87. N10 closed: research.md의 moai-ref-ui-polish H2 수치 11 -> 실측 13 정정. 괄호 안 열거(=10)는 원래 정확했고 앞 숫자만 과다 계상이었음을 각주로 기록. 형제 SPEC 2건이 재사용하는 문서라 상속 전에 차단. N8/N9는 사용자 결정에 따라 run-phase M6으로 연기 (산출물이 존재해야 수정 효과를 관측할 수 있는 AC 판정력 보강 유형). 🗿 MoAI * feat(SPEC-REF-SEO-ABSORB-001): M3 author moai-ref-seo skill body by clean-room rewrite Add the moai-ref-seo reference skill to the template tree as a single SKILL.md (207 lines, 12 H2: Target Agents + 8 domain sections + the invariant triad). Content is a clean-room rewrite: concepts and structure studied from the third-party source, every sentence newly written. Clean-room verification (template tree as the left side): - 8-gram shared: 0 - self-trip: source_pin_match=True, selftrip_shared_8grams=4600 (checker live) - LCS: 27 chars (bound 40) - structural divergence: PASS, recorded with reasoning in progress.md E.2 Guards: TestTemplateNoInternalContentLeak, TestSkillBodyLeakClassRecurrenceBackstop, TestLanguageNeutrality, TestSkillBodyNoLangReference, TestTemplateNeutralityAudit, TestTemplateNeutralityAuditC8Preserve all PASS. GOOS= template files still 2. Guidance is stated at the protocol and output layer (addresses, response headers, markup, serialized data) and names no web framework, so the language-neutrality regex cannot be tripped. Uncited figures, volatile stated values, the platform-bound listing flow, and generative-engine optimization are excluded; the accessibility operability items are delegated via the NOT for: clause. Catalog count constants, the local mirror, and docs-site rows are M4/M5 scope and are deliberately untouched; TestAllSkillsInCatalog therefore fails at this milestone (31 vs 32 skill directories) as expected. Status transition: draft -> in-progress on this first run-phase commit. 🗿 MoAI * chore: ignore worktrees in search + recognize worktree-* in Late-Branch Two unrelated-to-SPEC hygiene changes recovered from an uncommitted tree state and landed together because the catalog hash couples them: - .claudeignore: exclude .claude/worktrees/, since each worktree is a full checkout of this repo and every search hit was multiplied by the number of live worktrees. - manager-git.md: the Late-Branch detection cue now also matches worktree-* branches (moai cc -w names branches worktree-<name>). The local branch name is not load-bearing — Phase C mints the conventional feat/SPEC-* name at PR time either way. - catalog.yaml: regenerated hash for the manager-git.md edit. Template mirrors updated in the same commit per the Template-First rule. 🗿 MoAI * feat(SPEC-REF-SEO-ABSORB-001): M4 fill the six registration surfaces for moai-ref-seo Register moai-ref-seo across the same surface set moai-ref-secops actually occupies, in a single change so the markdown addition cannot skip the Go count guards. - catalog.yaml: five-key entry under optional-pack:frontend; hash generated by `make build` (gen-catalog-hashes --all), never hand-written - count constants: expectedSkillCount 31->32, expectedTotal 41->42, wantTotal 41->42 - delegation.yaml (local + template): domain_skills.frontend - workflow bodies (local + template mirrors): review.md --critique frontend spawn, sync/quality-gates-quality.md deep-review web-output conditional - skills_manifest_test.go: spot-check regression pin - local mirror .claude/skills/moai-ref-seo/SKILL.md, byte-identical to the template tree (sha256 39d3ad2b34...) skill-routing.md is deliberately untouched: moai-ref-secops does not appear there, so it is not a registration surface. Verification: the seven catalog guards each emit a --- PASS: line; a falsification round (constant reverted to 31) produced the expected FAIL, proving the guard is reachable rather than vacuously passing. Host and windows/amd64 builds exit 0; golangci-lint reports 0 issues against a 0-issue baseline; go test ./internal/template/... ok. 🗿 MoAI * docs(SPEC-REF-SEO-ABSORB-001): M5 add moai-ref-seo row to the 4-locale skill guide Adds one reference-table row per locale (en/ja/zh/ko) for the newly registered moai-ref-seo skill, completing the sixth registration surface. Prose category totals are deliberately left untouched: they already disagree with each other and with disk (en/ja/zh claim 27, ko claims 30, disk holds 32). That drift predates this SPEC and reconciling it is a separate concern — see plan.md §B.5. 🗿 MoAI * fix(SPEC-REF-SEO-ABSORB-001): correct AC-SEO-002 H1 judging command (N12) The H1 clause applied `grep -c '^# '` to the whole file, counting the frontmatter comment line `# MoAI Extension: Progressive Disclosure` as an H1. All 10 template-tree moai-ref-* skills carry `progressive_disclosure:`, so every one measures H1=2 and no ref skill could satisfy "exactly 1" — 9 of 9 precedents fail the command. Strip frontmatter before counting, reusing the `re.sub(r'\A---.*?^---\s*$', ...)` idiom already used by AC-SEO-011/012/013. Verified: all 10 skills yield body_h1=1. Threshold unchanged (1). `wc -l` (150-220) and `grep -c '^## '` (7-14) are unaffected by the defect and keep their commands and ranges; the H2 upper-bound derivation and the N4 correction note survive verbatim. 🗿 MoAI * docs(SPEC-REF-SEO-ABSORB-001): M5-M6 evidence — 24/24 AC PASS, structural-divergence record, N12 Record the M5 docs-site milestone and the M6 full acceptance sweep in progress.md, and populate the run-phase audit-ready signal. - M5: moai-ref-seo row added to the 4-locale skill guide; category prose totals deliberately left alone per the plan decision (pre-existing drift, not created here) - M6: all 24 acceptance criteria executed and quoted — 24 PASS, 0 FAIL, split across the clean-room protocol (7), content (8), and deployment guards (9) - AC-SEO-015: structural_divergence PASS recorded with the two required arguments (section ordering and table column composition both diverge from the source), since a bare PASS line is not accepted as a judgment - AC-SEO-022 closed at 15/15 surfaces — the four docs-site locales that were outstanding at M4 are now covered - N12: the AC-SEO-002 H1 count was a judging-command defect, not an artifact defect. Counting over the whole file scored the frontmatter progressive disclosure comment as an H1, so all ten reference skills failed 10/10. The command now strips frontmatter first; the threshold of 1 is unchanged. Run-phase signal: 24/0 AC, host and windows/amd64 builds pass, lint introduces no new findings, 17 implementation files across M3-M5. 🗿 MoAI * docs(SPEC-REF-SEO-ABSORB-001): mark the origin divergence figure as a point-in-time snapshot The recorded left-right count went stale while the run was still in progress: origin/main gained two more merges after the figure was written. Replace the fixed number with an explicit note that the value is a snapshot and must be re-measured at audit time. 🗿 MoAI --------- Co-authored-by: Goos Kim <manager-develop@mo.ai> | 1 个月前 | |
chore: coderabbitai request_changes_workflow 비활성화 coderabbitai가 CHANGES_REQUESTED 대신 COMMENT로만 리뷰하도록 변경. PR 머지 블로킹 악순환 방지 (dismiss_stale + strict + enforce_admins 조합). 추가 변경: enforce_admins도 false로 설정 (GitHub API로 적용 완료). 🗿 MoAI <email@mo.ai.kr> | 5 个月前 | |
fix(install): ship install.bat with CRLF line endings for cmd.exe The blanket '* text=auto eol=lf' gitattributes rule left install.bat stored and distributed as LF-only bytes. cmd.exe requires CRLF to parse multi-line parenthesized blocks reliably: on the first end-to-end CI run (run 31960480175) the LF-only copy made the parser desync through the whole script — the arg-parsing block executed tokens as commands, the banner lines failed as unrecognized commands, the version-resolution block was skipped entirely, and the literal !VERSION! reached the download URL. The defect predates this card; the new end-to-end step is what surfaced it. Commit the CRLF bytes verbatim and disable normalization for *.bat ('-text') so every distribution path — clone checkout, CI checkout, raw.githubusercontent.com — receives CRLF ('text eol=crlf' would keep the index LF and leave raw-URL downloads broken). The version-resolution payload is byte-identical after conversion (cmp, 589 bytes). Card: t53 (fixup). 🗿 MoAI | 22 天前 | |
merge(t82): AGENTS.md canonical contract layer for the Codex dual-harness SPEC-AGENTS-MD-CANON-001, M1-M6 plus the two M5 halves. A root AGENTS.md carries the standing contract every turn is bound by, whichever harness reads it. CLAUDE.md reaches it through the same @-import it already used for the config sections, so the contract is stated once and loaded once. A byte guard holds it under 24,576 B and fails the build on breach, because codex truncates the overflow silently and nothing else would ever signal it. The always-loaded budget guard now counts the file, so relocating a clause into it can no longer score as a diet that never happened. Eleven always-loaded documents were reduced to stubs against eight new lazy companions; the surface fell 8,555 tokens to 66,266. AC-AMC-018/019/020 stay open by requirement: REQ-AMC-014 fixes the achieved figure to a measurement on this branch once the batch is merged, not on a card worktree. AlwaysLoadedTokenBudget is untouched at 76,000. # Conflicts: # CHANGELOG.md | 17 天前 | |
chore(lint): pin golangci-lint v2 config and align rule-doc notes (#1500) Add a committed .golangci.yml at repo root so local (v2.10.1) and CI (v2.1.6) lint the SAME linter set deterministically despite the version skew. The enabled set is the long-standing golangci-lint default (errcheck, govet, ineffassign, staticcheck, unused) — present unchanged in both versions, so pinning removes the risk of a newer release silently expanding or shrinking the active linters. run.timeout is 5m (mirrors CI intent); errcheck settings are explicit so a future default flip cannot drift local vs CI. Proactive hardening — no active lint failure today (local default-config run is 0 issues both before and after this config). Rule-doc alignment (minimal, neutral): add a one-line note at each golangci-lint call site (agent-common-protocol-reference.md section 7 batch; manager-develop-prompt-template.md section C pre-check and section E5 self-verification) that a root .golangci.yml now governs the linter set + default timeout. The agent's --timeout=2m quick-check override is intentionally kept (CLI flag overrides config). Edits applied byte-identically to both the working copy and the template mirror. .golangci.yml is a root config, NOT templated (language-neutral template ships no Go lint config) — no mirror for it. CI v2.1.6 is the real compatibility test for the config; local verify ran on v2.10.1 only (0 issues, config verify clean). SKIP_MOAI_PRECOMMIT=1: this commit changes 0 .go source files (1 yaml config + 4 rule .md). Mirror-parity, sanitized-pair, and internal-content-leak tests run locally green (go test ./internal/template/... = ok 24.4s). 🗿 MoAI Co-authored-by: t <t@t.t> | 26 天前 | |
fix(release): stop publishing the raw commit list as the release body (#1590) * fix(release): stop publishing the raw commit list as the release body v3.1.1 shipped with a 49,000-character body of commit subjects. GoReleaser's changelog.use=github built it, and the phase meant to overwrite it never ran — it would have blocked on a Korean notes file nobody had authored, so the English notes were hostage to a translation step that was not happening. Three changes, one path: - .goreleaser.yml sets changelog.disable:true. The release now arrives with an empty body rather than a competing one. - Phase 7 of the release harness fills that body via `gh release edit --notes-file`, and is rewritten from extraction to composition. Neither source is safe to paste: the commit list buries what changed, and the CHANGELOG section is an internal record of SPEC ids, ACs, and coverage figures. The notes are written from the CHANGELOG's `### Summary`, which Phase 4 now carries an explicit obligation to keep complete. - The per-version Korean notes file is retired. release.sh no longer appends it, and the four docs-site changelog pages no longer claim GitHub Releases carries a Korean body. The existing v3.1.0.ko.md stays — the retirement is forward-looking. The docs-site pages are also restated as pointers rather than mirrors, so the per-release obligation there is a check that they still describe reality, not a copy into four locales that drifts silently. 🗿 MoAI * fix(release): watch this release's run, and stop waiting eventually Two review findings on the release-notes path, both real. The GoReleaser wait took the newest release.yml run rather than the one for the tag just pushed, so a re-run of an earlier tag or a concurrent release would be watched instead and its verdict reported as this release's. The lookup now resolves the tag to its commit and filters on it, and polls for the run rather than reading once five seconds after the push. The wait itself had no deadline: `gh run watch` blocks until the run ends, so a hung workflow held the release script open indefinitely. It is now a poll with a 45-minute ceiling — past the observed build time, short enough to surface a stall — after which the script detaches and says how to resume watching. Release Drafter's template still told maintainers to write CHANGELOG.md in English and Korean, which is the convention this path retired. It now states the English-only rule and names the `### Summary` section the harness composes the published notes from. 🗿 MoAI * docs(release-drafter): fix the dropped relative pronoun The draft notice read "This is a draft Release Drafter refreshes on every PR merge", which parses as a noun pile rather than a sentence. 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 18 天前 | |
feat(SPEC-MCP-DEFAULT-ON-001): moai MCP server as first-class default (plan+amendment+run) (#1455) * feat(SPEC-MCP-DEFAULT-ON-001): plan-phase artifacts (Tier M, 4 artifacts) Foundation SPEC of the moai-MCP first-class integration Epic (A -> B -> C). Inverts provisioning gate opt-in -> default-on, reduces template .mcp.json to single moai entry, adds .mcp.json to moai update 3-way merge set, reconciles docs. 10 REQ, 14 AC. plan_status: audit-ready. Markdown-only (SPEC artifacts); SKIP_MOAI_PRECOMMIT=1 per markdown-only policy. 🗿 MoAI * feat(SPEC-MOAI-MCP-SERVER-001, SPEC-TREND-MCP-001): in-place amendments for default-on gate inversion Both completed SPECs amended per SPEC-MCP-DEFAULT-ON-001 REQ-A-6/REQ-A-7: - SPEC-MOAI-MCP-SERVER-001: REQ-MCP-002 opt-in->default-on, REQ-MCP-015 opt-out flag, AC-MCP-002/006 amended (0.1.0 -> 0.2.0) - SPEC-TREND-MCP-001: REQ-TMC-003 default-on moai, REQ-TMC-001 active count 3->1, AC-TMC-001/004 amended (0.1.1 -> 0.2.0) status: completed preserved (owner direction); amendment_of omitted because the canonical transition pairs it with status in-progress, which the owner held at completed. HISTORY ### Amendments sub-section carries the record instead. Each amendment states its reversal rationale adjacent to the amended criterion (REQ-A-8). Load-bearing invariants byte-unchanged: REQ-TMC-002 secret hygiene, REQ-TMC-004 $comment-free clause, the neutral single-entry shape. Markdown-only (SPEC artifacts); SKIP_MOAI_PRECOMMIT=1 per markdown-only policy. 🗿 MoAI * feat(SPEC-MCP-DEFAULT-ON-001): M2 template single-moai entry + repo-root 4-entry divergence + neutrality guard inversion - internal/template/templates/.mcp.json: reduce active mcpServers to the single 'moai' entry {command: moai, args: [mcp-server]}; preserve $schema and staggeredStartup verbatim; no env block. - repo-root .mcp.json: add 'moai' alongside context7/chrome-devtools/playwright (the intentional permanent divergence per REQ-A-2 / plan.md §A.2). - internal/template/mcp_template_neutrality_test.go: invert mcpAllowedActiveKeys to {moai}; flip count assertion to 1; update required set to [moai]; forbidden-token regex set and ${VAR}-literal secret check preserved byte-unchanged (REQ-A-9). - make build regenerated catalog.yaml. 🗿 MoAI * feat(SPEC-MCP-DEFAULT-ON-001): M3 provisioning gate inversion - default-on with honored decline - wizard/questions.go: rename question ID mcp_tools_opt_in -> mcp_provision, flip Default false -> true, restate Title/Description as default-on positive (drop REQ-MCP-002 token from the en source). - wizard/types.go: MCPToolsOptIn -> MCPProvision (field rename). - wizard/wizard.go: dispatch case mcp_provision -> result.MCPProvision. - wizard/translations.go: rename the three locale keys (ko/ja/zh) to mcp_provision to match the new ID (content rewrite is M5). - core/project/initializer.go: InitOptions.MCPToolsOptIn -> MCPProvision. - init.go: provisionMCPEntryIfOptedIn -> provisionMCPEntryUnlessDeclined; param optedIn -> declined; gate if !optedIn -> if declined; call site passes !opts.MCPProvision; doc comment rewritten for default-on semantics. - init_mcp_provision_test.go: invert the OFF/ON tests to Default (declined=false provisions) + Declined (declined=true silent); reachability guard greps for the new function + field names. - wizard audit tests + expansion/restructure tests: follow the ID rename; mcp_audit DefaultOff test split (codex stays off, mcp_provision now on). - initializer_audit_test.go + init_audit_test.go: follow the field rename. RED captured pre-GREEN: undefined: provisionMCPEntryUnlessDeclined at init_mcp_provision_test.go:32/72/93 (compile failure). 🗿 MoAI * feat(SPEC-MCP-DEFAULT-ON-001): M4 add .mcp.json to moai-update 3-way merge set - update_template_sync.go: collectMergeableFiles now returns .mcp.json alongside .claude/settings.json and .moai/status_line.sh. A user's own MCP entries now survive `moai update` via the 3-way merge engine instead of being clobbered by the template deploy (REQ-A-4 / AC-A-012). - Correct the false comment that claimed 'MoAI no longer ships an MCP template' — the template .mcp.json IS shipped (deployer.go's no-dotfile WalkDir deploys it) and IS now a merge target (AC-A-013). - update_mcp_merge_test.go: new test guarding both the routing (collectMergeableFiles lists .mcp.json; false comment stays gone) and the behavior (MergeUserFiles preserves a user-added entry alongside the shipped moai entry). RED captured pre-GREEN: TestCollectMergeableFiles_IncludesMCPJSON failed on both the missing .mcp.json and the surviving false comment. 🗿 MoAI * feat(SPEC-MCP-DEFAULT-ON-001): M5 documentation reconciliation - default-on single-entry MCP story - wizard/translations.go: reword the three locale strings (ko/ja/zh) for default-on provisioning; drop the (REQ-MCP-002) token from every distributed locale string (neutrality). - settings-management.md (local + template mirror): restate the MCP Configuration paragraph for the single-entry default-on contract (one active moai entry; four third-party entries documented-but-disabled via moai mcp add; secret-hygiene invariant preserved). - template CLAUDE.md: reconcile §12 MCP inventory (lead with the default-on moai server; list Context7/claude-in-chrome/chrome-devtools/playwright/ ast-grep as one-command activations); annotate the selection-tree Context7 reference with its activation command. - make build regenerated catalog.yaml (no skill hashes changed). $25 neutrality verified: TestMCPNeutrality + TestInternalContentLeak PASS; no SPEC-ID/REQ-token/SHA/path leaks in the distributed template strings. 🗿 MoAI * chore(SPEC-MCP-DEFAULT-ON-001): draft -> in-progress + populate progress §E.2/§E.3 run-phase evidence All 14 AC-A PASS, 0 FAIL. M1-M5 complete on worktree-moai-mcp-integration (no push — Route B per repo-local-pr-policy; manager-git owns push+PR). 🗿 MoAI * test(SPEC-MCP-DEFAULT-ON-001): invert MCP-doctrine parity guard for default-on contract The mcp_doctrine_parity_test.go CI guard pinned the OLD opt-in default-off contract (asserted the paragraph carries 'opt-in'). SPEC-MCP-DEFAULT-ON-001 inverts the provisioning contract to default-on, so the guard must pin the new contract: assert 'default-on' presence instead of 'opt-in'. The retired sentence check + mcp-server + moai mcp add assertions are preserved. Cascade follow-up within the SPEC scope envelope (REQ-A-5: every surface that states the MCP default shall be reconciled — a test asserting the old default is itself such a surface). 🗿 MoAI * chore(SPEC-MCP-DEFAULT-ON-001): record baseline test-failure classification in §E.3 3 pre-existing baseline failures (zone-registry.md SPEC-ID + date leaks in internal/template; astgrep dogfood language stubs in internal/astgrep), all confirmed present at base 0e24dde06 and untouched by this SPEC. Recorded so the sync-auditor can distinguish baseline from new. 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 27 天前 | |
feat(SPEC-WORKTREE-BRANCH-GUARD-001): main-checkout branch-state guard via PreToolUse conditional deny (#1192) * docs(SPEC-WORKTREE-BRANCH-GUARD-001): plan-phase artifacts — spec/plan/acceptance/progress manager-spec authored; plan-auditor PASS 0.84; iteration-2 fixed D1-D7+N1-N2. Tier M, 6 milestones. Run-phase: M1-M2 then semi-autonomous checkpoint. * feat(SPEC-WORKTREE-BRANCH-GUARD-001): M1 branch-guard discriminant + exemption + regex set Adds internal/hook/branch_guard.go with: - isPrimaryCheckout(projectDir): primary-vs-worktree discriminant comparing absolute git-dir against absolute git-common-dir. Primary path uses --path-format=absolute (git 2.31+); dispatcher falls back internally to --absolute-git-dir + cwd-normalized --git-common-dir when older git rejects the flag (REQ-WBG-005). The fallback decision lives INSIDE the dispatcher so callers cannot bypass it (AC-WBG-005). - isExemptAgent(input): identity-based exemption for manager-git (HookInput.AgentType) OR MOAI_BRANCH_GUARD_EXEMPT=1 env var (REQ-WBG-011). No phase references (AC-WBG-011 word-boundary grep returns 0). - branchStatePatterns: named, blankable regex set (7 patterns) with the "non-flag token after subcommand" rule ([^\s-]) so list-only / path-restore invocations do not match (SPEC E-5). execCommand package-level indirection enables mock-injection for the fallback AC (AC-WBG-005). Tests (branch_guard_test.go): isPrimaryCheckout over real primary/worktree/ non-git (AC-WBG-002/004, real `git rev-parse` not a mock), fallback dispatcher via mock execCommand (AC-WBG-005), exemption paths (AC-WBG-003/011), and regex true-positive / true-negative coverage including the blankable contract for M6's deny-origin test. Frontmatter status: draft -> in-progress on spec/plan/acceptance (the only SPEC-artifact edit allowed for this transition). M2 (checkBranchState wiring into pre_tool.go + integration tests) and M3-M6 (rule bump, template mirror, CLI advisories, .worktreeinclude, latency/deny-origin tests) are out of scope for this spawn. 🗿 MoAI * feat(SPEC-WORKTREE-BRANCH-GUARD-001): M2 wire checkBranchState into PreToolUse handler Extends branch_guard.go with: - checkBranchState(input, projectDir): returns DecisionDeny + the "BRANCH_GUARD_VIOLATION: <suffix> in primary checkout (...)" sentinel reason when ALL THREE hold: primary checkout, branch-state pattern matched, invoking agent not exempt. Fails OPEN (allow + stderr advisory + audit-log append to .moai/logs/branch-guard-audit.log) on any git-context uncertainty (REQ-WBG-012). - extractBranchStateCommand + appendBranchGuardAdvisory helpers. Wires checkBranchState into preToolHandler.Handle AFTER the existing checkBashCommand block and BEFORE the default-allow fall-through, gated on input.ToolName == "Bash" (REQ-WBG-001, REQ-WBG-010). The new deny completes on the fast regex path (microseconds) and never invokes the census P1-B quality-gate path (structurally inaccessible to branch-state commands). Integration tests (pre_tool_test.go extension): - TestBranchGuard_DeniesGitSwitchInPrimary (AC-WBG-001) + paired exempt-env allow arm (vacuous-pass guard). - TestBranchGuard_AllowsInWorktree (AC-WBG-002) via real `git worktree add`. - TestBranchGuard_ExemptAllow (AC-WBG-003/011): checkBranchState unit-level exemption for `git reset --hard origin/main` + AgentType=manager-git AND env var; plus end-to-end Handle allow via a non-ask command. NOTE: `git reset --hard` is in the pre-existing AskBashPatterns, so the OVERALL handler returns DecisionAsk before the branch-state check for that literal command; AC-WBG-003 is satisfied at the branch-state layer (documented in §E Gaps). - TestBranchGuard_FailOpenOnGitError (AC-WBG-012): non-git projectDir returns allow + writes the audit-log advisory. branch_guard.go coverage: 91.7% (>= 85% AC threshold). M3-M6 (rule v1.1.0 bump, template mirror + mirror-test allowlist, CLI advisories, .worktreeinclude reconciliation, latency + deny-origin integration tests) remain out of scope for this spawn. 🗿 MoAI * docs(SPEC-WORKTREE-BRANCH-GUARD-001): record Phase 4 mode selection + M1-M2 verification evidence - §F Phase 4 Mode Selection: Mode 5 (sub-agent sequential) — coding-heavy - §E.2: run-phase entry (pre-spawn sync + merge), M1-M2 commits, orchestrator independent verification (build/vet/test/lint/cover green), Gap-G1/G2 analysis 🗿 MoAI * feat(SPEC-WORKTREE-BRANCH-GUARD-001): M3 rule v1.1.0 + template mirror byte-parity Bump main-checkout-branch-guard.md Version 1.0.0 -> 1.1.0 and add the 'Mechanical Enforcement (v1.1.0)' section documenting the PreToolUse hook as the enforcer (handler path, BRANCH_GUARD_VIOLATION sentinel, primary-vs- worktree discriminant, manager-git + MOAI_BRANCH_GUARD_EXEMPT exemption, fail-open norm). Cross-reference SPEC-WORKTREE-BRANCH-GUARD-001. Mirror byte-identically to internal/template/templates/ and enroll in the workflowOptMirroredPaths allowlist so CI enforces parity on every subsequent edit (REQ-WBG-006, REQ-WBG-007). Re-embed via make build; TestRuleTemplateMirror PASS, falsification arm (introducing 1-byte drift) FAILs as expected then reverts. 🗿 MoAI * feat(SPEC-WORKTREE-BRANCH-GUARD-001): M4 CLI worktree advisory (init/update/web) Add emitWorktreeAdvisory(out, projectRoot) consuming workflow.worktree.auto_create. When auto_create is true the advisory phrases as an auto-creation notice; when false (current default — L1 worktree is Claude Code runtime-autonomous, MoAI does not auto-create) it stays a recommendation. Either phrasing matches the AC-WBG-009 regex (worktree.*isolation | use a worktree | moai (cc|cg) -w | claude --worktree). Wired into: - moai init (stdout, after success card) - moai update (stdout, on completion AND on the --dry-run path so the smoke run observes the advisory without mutating the filesystem) - moai web (stdout, on startup — the Web Console runs in the shared primary checkout) AC-WBG-009 verification (built binary): init --non-interactive <tmp>: grep -c matches = 1 (>= 1 PASS) update --dry-run: grep -c matches = 1 (>= 1 PASS) web --port <unused> --no-open: grep -c matches = 1 (>= 1 PASS) Falsification arm: stubbing emitWorktreeAdvisory to a no-op drops all three grep counts to 0, then restoring returns them to 1. C-HRA-008: no AskUserQuestion/mcp__askuser added to internal/cli/ (baseline boundary grep count unchanged at 32 — all pre-existing comments/testdata). 🗿 MoAI * feat(SPEC-WORKTREE-BRANCH-GUARD-001): M5 reconcile .worktreeinclude with template SSOT Track both .worktreeinclude copies (neither was previously committed) with byte-identical content from the template SSOT, removing the local teammateMode mention (redundant — §22.3 documents teammateMode as a settings.local.json field, and .worktreeinclude already copies that file) and keeping the template's 'Add your own project-local files' guidance. REQ-WBG-008 / AC-WBG-008: diff .worktreeinclude internal/template/templates/.worktreeinclude → exit 0 grep -c teammateMode .worktreeinclude → 0 grep -c 'Add your own project-local files' template/.worktreeinclude → 1 Falsification: appending 'teammateMode' to the local copy makes diff return non-zero (verified, then reverted). Both files now staged in the same M5 commit (plan.md §F M5 'commit both in the same sync-phase commit' — collapsed into the run-phase M5 commit per the delegation's per-milestone commit pattern). 🗿 MoAI * fix(SPEC-WORKTREE-BRANCH-GUARD-001): M3 sanitized-pair resolution for §25 conflict The M3 commit (7fd49d48f) enrolled main-checkout-branch-guard.md in the workflowOptMirroredPaths byte-parity allowlist with a byte-identical template mirror. TestTemplateNoInternalContentLeak + TestRuleProvenanceAudit then correctly flagged the template mirror for §25 neutrality violations (SPEC-WORKTREE-BRANCH-GUARD-001 + REQ-WBG-001..013 tokens). The delegation's premise ('§25 SPEC-ID neutrality does NOT bind .claude/rules/ mirrored files') was factually wrong: the §25 guards DO scan the template mirror, and every existing byte-mirrored rule (spec-workflow.md, session-handoff.md, hooks-system.md, model-policy.md) carries 0 SPEC-IDs in BOTH trees. The established pattern for rules that need internal provenance in the source is the sanitized-pair registry (sanitized_pair_parity_test.go), not byte-parity. Resolution (matches the runtime-recovery-doctrine.md / zone-registry.md precedent): - Source rule RETAINS SPEC-WORKTREE-BRANCH-GUARD-001 + REQ-WBG tokens (AC-WBG-006 traceability grep still hits: count=2). - Template mirror is §25-sanitized (SPEC-ID + REQ tokens stripped to generic prose; the Mechanical Enforcement doctrine is preserved verbatim, only the Origin + cross-reference lines are reworded). - Rule REMOVED from workflowOptMirroredPaths (byte-parity cannot hold). - Rule ADDED to sanitizedPairPaths so TestSanitizedPairParity enforces doctrine parity (structural drift detection, not byte-identity). - Mirror cleanliness enforced by TestTemplateNoInternalContentLeak. BLOCKER (surfaced for sync phase): AC-WBG-007 as written requires byte-parity + workflowOptMirroredPaths enrollment, which is incompatible with §25 neutrality for a rule that references its own SPEC-ID. The AC wording needs a manager-spec amendment (switch AC-WBG-007 to the sanitized-pair registry path). The codebase is green either way; this commit implements the resolution that keeps CI green. Verification: AC-WBG-006 source: Version=1.1.0(1) Mechanical Enforcement(1) SPEC-ID(2) ✓ §25 template mirror: SPEC-ID(0) REQ-WBG(0) ✓ TestTemplateNoInternalContentLeak PASS, TestRuleProvenanceAudit PASS, TestSanitizedPairParity PASS (main-checkout within reword tolerance), TestRuleTemplateMirror PASS (rule no longer in its allowlist). 🗿 MoAI * test(SPEC-WORKTREE-BRANCH-GUARD-001): M6 branch-guard characterization + integration + latency + regression Add the three M6-tests deliverables plus two coverage-backfill tests: NEW internal/hook/pre_tool_branch_guard_integration_test.go: - TestBranchGuard_QualityGateNotInvoked (AC-WBG-010 arm 1, census P1-B structural guarantee): asserts quality.IsGitCommit returns false for every branch-state command (switch/checkout/branch/reset/stash/rebase/ merge). Falsification arm: if a future refactor widens IsGitCommit to match any of these, the test FAILs. Sanity arm confirms git commit is still recognized (no always-false regression). - TestBranchGuard_Latency (REQ-WBG-010): internal for-loop over 100 iterations, each checkBranchState call <= 500ms (10x headroom under the 5s PreToolUse budget). Prints 'per-invocation: worst=Xms avg=Yms'. Measured worst=24.6ms avg=16.4ms on the test host. NOT a Benchmark. - TestBranchGuard_CheckBranchStateOrigin (AC-WBG-010 arm 2, deny-origin falsification): with branchStatePatterns blanked, the same git switch event that is otherwise denied returns allow — proving the deny comes from checkBranchState, NOT checkBashCommand's DangerousBashPatterns or the quality gate. Restore via t.Cleanup. EXTEND internal/hook/branch_guard_test.go (coverage backfill): - TestIsPrimaryCheckout_PartialPrimaryPath: covers the dispatcher's second-primary-call-failed fall-through (isPrimaryCheckout 88.2%->94.1%). - TestIsPrimaryCheckout_FallbackAbsoluteGitDirFail: fallback --absolute- git-dir failure branch (fail-open signal). - TestIsPrimaryCheckout_FallbackCommonDirFail: fallback --git-common-dir failure branch (fail-open signal). - TestAppendBranchGuardAdvisory_WriteFailure: OpenFile failure branch (appendBranchGuardAdvisory 83.3%->94.4%). Coverage: internal/hook/branch_guard.go per-function now all >= 94.1% (isPrimaryCheckout 94.1%, appendBranchGuardAdvisory 94.4%, the rest 100%); file-level well above the 85% AC §C gate. AC-WBG-003 note (Gap-G1, documented): the end-to-end git reset --hard allow is NOT satisfiable through Handle because git reset --hard is in the pre-existing AskBashPatterns (pre_tool.go:250) — checkBashCommand returns DecisionAsk before the branch-state check. This is the documented Phase-D interim break (acceptance.md F-1). The exemption is proven at the branch-state layer (TestBranchGuard_ExemptAllow unit subtests) and via a non-ask command (git switch main) end-to-end. 🗿 MoAI * docs(SPEC-WORKTREE-BRANCH-GUARD-001): M3-M6 run-phase evidence + audit-ready signal - §E.2: M3-M6 commits, orchestrator independent verification (build/vet/test/lint green, LSP wrong-tree twice resolved), §25 sanitized-pair resolution - §E.3: run-phase functionally complete; 11 AC PASS + AC-003 branch-layer + AC-007 PASS-WITH-DEBT; two sync-phase actions (REQ-WBG-007 amendment, AC-003 Gap-G1 note) 🗿 MoAI * docs(SPEC-WORKTREE-BRANCH-GUARD-001): amend REQ-WBG-007 byte-parity → sanitized-pair REQ-WBG-007/AC-WBG-007 previously required the template mirror to be byte-identical to the source rule and enrolled in workflowOptMirroredPaths. This was based on a false premise: §25 (TestTemplateNoInternalContentLeak) forbids a template mirror from carrying an internal SPEC-ID + REQ tokens. Every existing byte-mirrored rule achieves byte-parity because the source is ALSO §25-clean; this rule's source is NOT §25-clean (it carries the SPEC-ID for REQ-WBG-006 traceability). Resolution (sanitized-pair, matches runtime-recovery-doctrine/zone-registry precedent): the source rule retains the SPEC-ID + REQ tokens; the template mirror is §25-sanitized (SPEC-ID line → generic prose, SPEC-ID cross-ref bullet dropped); the rule is enrolled in sanitizedPairPaths (NOT workflowOptMirroredPaths). TestSanitizedPairParity enforces doctrine parity; TestTemplateNoInternalContentLeak enforces the mirror's §25 cleanliness. Implementation is already CI-green (run-phase landed the mechanism). This is a wording/AC correction to make the SPEC match reality, not a design change. Verification: - AC-WBG-006 (source SPEC-ID grep): grep -c = 2 (unchanged, PASS) - AC-WBG-007 sanitizedPairPaths enrollment: grep -c = 2 (>= 1 PASS) - AC-WBG-007 source SPEC-ID present: grep -c = 2 (>= 1 PASS) - AC-WBG-007 mirror SPEC-ID absent: grep -c = 0 (PASS, §25-clean) - TestSanitizedPairParity: ok 0.685s - TestTemplateNoInternalContentLeak: ok 0.790s - moai spec lint --strict (all 3 artifacts): 0 findings 🗿 MoAI * docs(SPEC-WORKTREE-BRANCH-GUARD-001): sync-phase artifacts — 3-phase close CHANGELOG entry (Added: PreToolUse conditional-deny branch-state guard + CLI worktree advisory + .worktreeinclude reconciliation + doctrine rule v1.0.0 → v1.1.0 + template mirror §25 sanitized-pair). progress.md §E.4 Sync-phase Audit-Ready Signal populated (sync_commit_sha: pending-backfill-SPEC-WORKTREE-BRANCH-GUARD-001). Frontmatter status: in-progress → implemented → completed on spec.md / plan.md / acceptance.md (merged 3-phase close); updated: refreshed. 13 ACs: 11 PASS + AC-WBG-003 branch-state-layer PASS (Gap-G1 documented Phase-D interim break) + AC-WBG-007 PASS-WITH-DEBT (sanitized-pair, REQ amended in 4764e2579). 🗿 MoAI --------- Co-authored-by: Goos Kim <manager-develop@mo.ai> | 1 个月前 | |
feat(spec-agents-md-canon-001): M2 — root AGENTS.md contract layer (t82) Author the single root AGENTS.md carrying all 35 Codex-relevant clause blocks from M1's classification, plus B042 promoted K->C under spec.md §D.2's "when in doubt, classify to the Codex side" constraint. Landed at 14,229 B — 57.9 % of REQ-AMC-004's 24,576 B ceiling, 18,539 B of headroom against the measured 32,768 B codex budget. Arm B identity re-evaluated at the landed size (not the M1 projection): required cut = 71,207 + 14,229/4 - 66,371 = 8,393 tok available = 10,670 tok (nine never-stub-split files, 38.0 % precedent) margin = +2,277 tok, on the first term alone Verification (this tree, 57a7ef71b): wc -c AGENTS.md -> 14229 presence.py -> clauses checked: 36 | missing: none trace.py -> mapped rows: 36 | unmapped C: none structure.py -> structure 1179 / clause text 13050 surface_r3.py -> guard tokens 71207 (unchanged) go test ./internal/config -run 'Budget|AlwaysLoaded' -> ok .gitignore: the blanket AGENTS.md ignore is removed — the root file is this repository's tracked contract document and M6 mirrors it to users. Out of scope (later milestones): the CLAUDE.md import layer (M3), the guard enumeration extension (M5), the template mirror (M6). | 17 天前 | |
docs(SPEC-CI-FLAKE-SERIES-001): sync-phase — implemented, observation window pending (t278) (#1670) * feat(SPEC-CI-FLAKE-SERIES-001): plan-phase artifacts (M, 4 artifacts) (t278) SPEC-CI-FLAKE-SERIES-001 v0.2.0 (card t278, absorbing t270/t271): spec.md (REQ 12, GEARS) + plan.md (M1-M4) + acceptance.md (AC 10) + progress.md (§E.1 audit-ready, §F mode: serial, semi-autonomous). Plan-audit iter-1: PASS-WITH-DEBT 0.86 (Tier M threshold 0.80), SHOULD-FIX D1/D2 + MINOR D3-D5 all resolved in v0.2.0. Implementation Kickoff Approval granted by operator 2026-08-26. Evidence: .moai/reports/t278/plan-audit-iter1.md. 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): M1 investigation artifacts (t278) Baseline sweep (attempt-aware, 537 runs 2026-08-10..26, 556 attempts): 4 current-defect flare-ups post-#1591 (p-hat 4/166); 2 pre-#1591 bilateral 2.72x occurrences found and excluded by timeline. Statistic decision: AND-gate adopted (per-round median AND ratio-of-medians over MaxUnits), fallback trimmed-median rejected on measured arithmetic. Artifacts: forensics.md, reproduction-rate.md, timing-statistic-decision.md, sweep-attempts.sh (v2), refetch-jobs.sh; progress.md E.2 evidence. 🗿 MoAI * fix(SPEC-CI-FLAKE-SERIES-001): M2 stop-rule re-poll closes poller TOCTOU (t278) TestConcurrentSendPoll's poller treated an empty-drain observation as terminal when sendersDone merely looked closed: the observation and the channel check are two instants, and the final Sends' pending renames can land between them (CI run 32774108273 a1: 97/100 loss, zero poll errors, 0.07s). REQ-CFS-001: a closed channel now triggers one re-poll started after all senders completed; only ITS 0/0 terminates, and its claims merge into the collected set. The stop rule is extracted into pollUntilDrained so the new TestPollerStopRule drives the same code path through a channel-handshake forced interleaving (97 sends -> 0/0 observation -> final 3 sends + close -> check). Observed on this tree: RED 97/100 pre-fix, GREEN 100/100 post-fix, mutant (old select-immediate-exit form restored) RED 97/100 again, restore GREEN. Carries the draft -> in-progress frontmatter transition (first run-phase code commit). 🗿 MoAI * fix(SPEC-CI-FLAKE-SERIES-001): M2 RT005 latency assertion rebuilt as 20-sample p95 (t278) The REQ-HAE-002 latency contract is p95 <= 100ms (AC-HAE-003), but TestConfigChange_RT005ReloadIntegration enforced a single-sample wall-clock maximum — a category error that turned one ~120ms scheduler preemption into a CI failure on a us-scale sync path (run 32815411885 a1: 123.195919ms, 0.15s test duration). REQ-CFS-007: the assertion now evaluates the contract's own statistic — nearest-rank p95 (19th of 20 = second-largest) over 20 fresh-handler samples, each sample's async goroutine drained via testutil.WaitForAsync before the next. One preempted sample cannot move the second-largest; two must land together. Mutant proof on this tree: time.Sleep(150ms) injected in the sync path -> RED (p95 151.163958ms over 20 samples); removed -> GREEN (-race -count=5 -run TestConfigChange, 5/5). 🗿 MoAI * fix(SPEC-CI-FLAKE-SERIES-001): M2 paired calibrated arm enforces AND-gate (t278) The calibrated arm of AssertPaired failed on the median of the per-round ratios alone. That estimator survives offset load steps but is inflated by noise phase-locked to the ref-first/fn-first alternation: half the rounds' ratios inflate while both medians stay level — CI observed a 2.47x per-round median against 1.09x medians on byte-identical code (run 32779472351 a1, the flake this SPEC exists to close). Per the M1 statistic decision (.moai/reports/t278/timing-statistic-decision.md): the paired path now computes BOTH figures and fails only when BOTH exceed MaxUnits (CheckRatioAnd). Each figure is individually robust to one noise class, so their agreement refuses each single-figure false positive while keeping bilateral regression detection — the pre-#1591 bilateral form (2.72x both figures) still fails, detection preserved. CheckRatio's pure-function signature and semantics are unchanged (the unpaired Assert/Check path and the existing pin tests paired_step_test.go:56/:79 compile and pass untouched); the p95/worst arms are factored into checkAbsolute shared by both gates. The paired log line now reports both figures for diagnosability. Property tests (paired_asym_test.go, synthetic distributions — no measurement, parallel-safe): (a) alternation-locked asymmetry, true ratio 1.00x — AND-gate passes AND a falsifier arm proves the per-round-alone estimator still trips on it (observed pre-fix on this tree: RED 'calibrated gate tripped ... 2.50x'); (b) homogeneous 3x — AND-gate still fails. Existing pins TestCalibratedRatioSurvivesOffsetLoadStep (load-step) and TestPairedRatioStillCatchesRealCostGrowth (4x) pass unchanged. All 3 AssertPaired caller sites green: paired_test.go:61 (1.00x/1.12x), observer_test.go:251 (0.99x/0.98x), pre_tool_branch_guard_integration_ test.go:207 (1.00x/1.01x). 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): M2 run-phase evidence in progress.md (t278) Per-fix RED/GREEN/mutant observations with verbatim outputs and tree SHAs, E-battery results on final tree 324883ebb, AC-CFS-010 marker-rule judgment (0 violations), and the M2 commit list. 🗿 MoAI * fix(SPEC-CI-FLAKE-SERIES-001): add Out of Scope subsection for spec-lint (t278) Restructure section 4's out-of-scope prose into a compliant '### 4.1 Out of Scope' subsection (6 list items) to clear the spec-lint MissingExclusions ERROR on PR #1666. Content unchanged; in-scope prose stays as the section intro. Version 0.2.1. Local verification: go run ./cmd/moai spec lint --strict -> exit 0, 0 findings for SPEC-CI-FLAKE-SERIES-001, 0 ERRORs repo-wide. 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): complete D2/AC-count residue from audit iter1 (t278) Finish two leftovers of the plan-audit iter1 fix pass (spec v0.2.0 claimed both applied; one edit had failed on an exact-match miss and the plan Tier table was not updated): - acceptance.md AC-CFS-007 matrix row: pin N = go_code=true workflow run count (matches REQ-CFS-010 and the section D.1 GREEN cell) - plan.md Tier table: AC count 9 -> 10 (AC-CFS-010 added by D1) 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): M3 merged 379b310a6 + M4 series analysis + window opened (t278) M3 closed: PR #1666 squash-merged (operator-approved), post-merge observation window opened at 2026-08-26T18:05:57Z, first run 32997835484. M4 artifacts: series-analysis.md (REQ-CFS-012 common factor finalized, reusable authoring rule named) + reproduction-rate.md §5 ledger opened. Companion cards t270/t271 closed as absorbed by this SPEC. 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): sync-phase artifacts — implemented, AC-CFS-007 window pending (t278) Sync-audit PASS-WITH-DEBT 0.942 (9 GREEN / 1 PENDING / 0 RED); the completed transition is deferred until the observation window closes (earliest 2026-09-02T18:05:57Z AND N>=40 go_code=true runs) — operator decision 2026-08-27. CHANGELOG entry, progress.md §E.4 signal, spec.md frontmatter in-progress -> implemented, and the sync-audit report. 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): backfill sync_commit_sha 3a7aaab37 (t278) 🗿 MoAI * docs(SPEC-CI-FLAKE-SERIES-001): post-merge ledger first accrual N=2 (t278) Runs 32997835484 (Test/Race green before concurrency-cancel; valid observation) + 32999196269 (all green). 0 recurrences. 2/40 runs, 0/7 days. Run 33000104346 pending accrual. 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 12 天前 | |
docs: point the ci-loop skill at its own protocol twins The two ci-autofix-protocol.md files are not an unresolved duplicate. They carry disjoint paths: scopes — the dev original governs scripts/ci-autofix through the ci-loop skill, the distributed one governs the manager-develop autofix cycle — so they never load together, and the earlier move out of the update-managed roots already removed the path collision that could overwrite the dev original. Keeping both is the decision SPEC-CI-LOOP-DEVONLY-001 recorded: a rule belongs only where the thing it governs exists. What was actually wrong was documentation pointing at the wrong twin. The ci-loop skill cited the distributed autofix rule, which is deliberately script-free and says nothing about the scripts the skill runs, and cited a watch-protocol path that no longer exists anywhere — that file stopped shipping and now lives only under rules/local. Both citations now name the dev twins and say why the same basename elsewhere is a different rule. The local guide still declared the situation unresolved, which is what keeps generating cards against it; it now records the resolved state. The incident record a few lines below is left alone — it describes what happened, not what is true now. No template mirror is owed: neither edited file has one. card: t159 | 17 天前 | |
feat(spec-agents-md-canon-001): M4 — relocate detail to lazy companions (t82) Eight new paths:-scoped lazy companions plus a second pass into two that already existed. Every stub keeps its obligations and leaves a pointer; only rationale, procedure, worked examples, incident records, and long cross-reference tables moved. main-checkout-branch-guard 11,865 -> 6,395 (-5,470) moai-mcp-tools 7,357 -> 2,389 (-4,968) verification-claim-integrity 13,140 -> 8,224 (-4,916) cross-session-messaging 16,672 -> 11,823 (-4,849) context-window-management 13,009 -> 8,828 (-4,181) moai-constitution 18,958 -> 15,433 (-3,525) agent-common-protocol 27,043 -> 24,645 (-2,398) askuser-protocol 23,504 -> 21,822 (-1,682) native-idiom-and-register 4,967 -> 3,952 (-1,015) CLAUDE.md 20,748 -> 19,766 ( -982) skill-routing 5,825 -> 5,595 ( -230) total 163,088 ->128,872 (-34,216 B) Ratchet target met: always-loaded surface = 66266 tokens (budget 76000, headroom 9734, 18 entries) required cut 8,450 <= achieved 8,555 -> margin +105 Both identity terms were needed. The nine never-stub-split files yielded 7,534 tokens — 916 short of the required cut — and the second pass into already-split files supplied 1,020 (6.0% of their remainder, above the 5% assumed). The 38.0% pilot ratio was measured on kanban-dispatch.md, the most rationale-dense file in the set; the never-split nine are more obligation-dense than that sample. AC-AMC-011: all 8 new companions grep clean for [HARD]/MUST/MUST NOT/shall. An initial append into askuser-protocol-reference.md carried a duplicate [HARD] line and was removed — that companion already owned the section. Template mirrors land here, not in M6. TestSanitizedPairParity went red on the first two stubs (correctly — it exists to catch doctrine that fails to reach the mirror); 4 registry members had drifted. All 16 rule files and all 8 companions are mirrored, make build has run, suite green. Byte-copy was used only where the template was verified byte-identical to the pre-edit local copy; the two genuine sanitized pairs received the same structural edit re-sanitized, never a copy. Verification: go test ./internal/config/ -> ok 17.739s go test ./internal/constitution/ -> ok 0.486s go test ./internal/hook/ -> ok 30.409s go test ./internal/template/ -> ok 35.697s go test ./internal/cli/ -> ok 334.733s go vet ./internal/config/ ./internal/template/ -> clean no _test.go modified Known gap: internal/template/templates/CLAUDE.md is still unmirrored and now two changes behind (M3's @AGENTS.md import + M4's compression). Both land in M6 with the template AGENTS.md, per the M3 note. Residual risk: the margin is 105 tokens (0.16%). Both levers remain sized — the never-split nine retain 29.4% of projected yield, the already-split remainder was drawn down 6.0%. Untouched: .claude/output-styles/moai/moai.md, 61,706 B and 23% of the surface, excluded from M1's bound and outside M4's rules scope. | 17 天前 | |
fix(docs): fix broken links — repo rename and stale Claude Code URL - Fix GitHub repo URLs: modu-ai/moai-adk-go → modu-ai/moai-adk (CONTRIBUTING.md, CONTRIBUTING.ko.md, docs-site/hugo.toml, docs-site/layouts/partials/site-header.html) - Fix stale Anthropic docs URL in all 4 README files: docs.anthropic.com/en/docs/claude-code → code.claude.com/docs/en SPEC-V3R6-LINK-FIX-001 🗿 MoAI | 1 个月前 | |
docs: fix phrasing in CONTRIBUTING.md (#1130) Co-authored-by: ferkans-amir <amir.rezaei@tu-berlin.de> Co-authored-by: Goos Kim <email@goos.kim> | 1 个月前 | |
chore: 라이선스를 GPL-3.0에서 Apache-2.0으로 변경 (#570) * chore: 라이선스를 GPL-3.0에서 Apache-2.0으로 변경 상업적 수정 사용을 허용하기 위해 프로젝트 라이선스를 변경. - LICENSE: GPL-3.0 전문을 Apache-2.0 전문으로 교체 - README (en/ko/ja/zh): 배지 및 본문 라이선스 참조 업데이트 - CONTRIBUTING (en/ko): 기여 라이선스 조항 업데이트 - 스킬 3종 (moai-platform-deployment, moai-design-tools, moai-platform-auth): license: MIT → Apache-2.0 통일 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> 🗿 MoAI <email@mo.ai.kr> * feat(cli): --permission-mode 플래그 추가 및 스킬/설정 업데이트 CLI 런처에 --permission-mode 플래그를 추가하여 Claude Code의 다양한 권한 모드(default, acceptEdits, plan, auto, bypassPermissions, dontAsk)를 직접 지정할 수 있도록 개선. 기존 --bypass 플래그는 bypassPermissions의 단축키로 유지. - cc, cg, glm 명령에 --permission-mode 플래그 지원 - GLM에서 auto 모드 사용 시 명확한 에러 메시지 제공 - syncBypassToSettingsLocal을 syncPermissionModeToSettingsLocal로 리팩토링 - profile preferences에 PermissionMode 필드 추가 - design-tools, shadcn 스킬 문서 업데이트 - config sections (git-convention, language, llm, quality) 구조 개선 🗿 MoAI <email@mo.ai.kr> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> | 5 个月前 | |
feat(SPEC-CODEX-DUAL-AGENTS-001): M3 mass emission + guards Emit the 11 Codex agent TOMLs from the neutral .md layer and commit them under templates/.codex/agents/moai/ (P-04-confirmed subdirectory layout). - golden_test.go: drift guard (emission vs committed, sha256; regeneration via AGENTEMIT_UPDATE=1 / make agents-emit), .md identity + tree-unmodified (AC-001/AC-002), real-set shape (7 MCP carriers, effort map, model omitted, sandbox workspace-write - AC-007/008/009), body byte-equality (AC-003), embed-FS presence + byte equality (AC-010), neutrality-by- inheritance (AC-011: emitter introduces no new token; the delegation's literal zero-count grep is unsatisfiable under R-005 verbatim bodies - counts mirror the .md sources, recorded in progress.md E.2), publication path hygiene - codex_agents_deploy_test.go: AC-010 deploy fixture - full template Deploy into t.TempDir() lands .codex/agents/moai/ with 11 byte-equal TOMLs - .gitignore: negate the bare .codex/ rule for the template subtree so the artifacts are committable (root and template gitignores verified not mirror-paired) - Makefile: agents-emit regeneration target All internal/template tests green; windows build, vet, lint clean; make build verified (catalog.yaml unchanged). 🗿 MoAI | 17 天前 | |
feat(t289): GLM-5.3-Flash default coding model — SPEC-GLM-FLASH-DEFAULT-001 (max-only reasoning, 1M context) (#1668) * fix(SPEC-GLM-FLASH-DEFAULT-001): plan-audit iter-1 defects D1-D7 applied (card t289) 🤖 Generated with [Claude Code](https://claude.com/claude-code) 🗿 MoAI * chore(SPEC-GLM-FLASH-DEFAULT-001): kickoff gate record + Phase 4 mode selection log + iter-2 verdict line (card t289) 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M1 register glm-5.3-flash and switch tier-slot defaults Add DefaultGLM53Flash / DefaultGLM53 named constants; retarget all seven tier-slot constants (High/Medium/Low/Fable + legacy Haiku/Sonnet/Opus) to glm-5.3-flash; ValidGLMModels() offers the 5-model set with glm-5.3 retained as an explicit member (derived-set drop guard). Template llm.yaml twin slots switched. Web i18n option labels added x4 locales (schema-parity guards require them once the set offers flash). Coupled tests updated. card: t289 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M2 flash max-only effort overlay branch Model-aware overlay: IsGLMFlashModel + CollapseClaudeEffortToGLMForModel / ResolveGLMReasoningForModel / SessionGLMReasoningStateForModel return the max state for EVERY Claude effort (incl. low) when the resolved model is glm-5.3-flash — flash accepts reasoning_effort: max only; non-flash collapse unchanged (low→low, above→max, unrecognized→max). Wire path threaded (resolveGLMBackendForLaunch returns the high slot; buildEnvForGLMLaunch / glmReasoningEnvVarsForModel) and both display surfaces (agentfm chip, model profile report) use the same model-aware resolution so display and wire agree. Template llm.yaml overlay comment documents the flash rule (documentation twin). setGLMEnv needs no threading: its session default is max under every model. card: t289 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M3 explicit glm-5.3-flash context window entry Add "glm-5.3-flash": 1_000_000 to glmContextWindows as an explicit entry (divergence guard) rather than relying on the glm-5.3 longest-substring match; registration-time guidance comment added. Table tests pin flash→1M direct, glm-5.3→1M retained, and an unregistered glm-5.3-* variant inheriting 1M via substring. card: t289 🗿 MoAI * test(SPEC-GLM-FLASH-DEFAULT-001): M4 flash web-label count pin (8 = 2 key families x 4 locales) The flash option labels (i18n.js) and the 5-model tier-set assertion landed with M1 — the schema-parity guards (TestDataI18nKeysSubsetOfDictionary / TestI18nKeySetParity) fail the moment the derived set offers flash, so a green M1 commit required them. This M4 commit adds the AC-010 mechanical pin: exactly 8 non-empty flash labels across f.llm.glm.models.opt.* and f.workflow.audit.glm.model.opt.* in en/ko/ja/zh. card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): M5 name glm-5.3-flash as default in README + docs-site (4-locale) Grep-gated to the surfaces that name the default model or enumerate the GLM set: README x4 (model enumeration + tier table + selectability note), docs-site multi-llm/_index x4, advanced/config-sections x4 (yaml example), advanced/profile-matrix x4 (fable binding + flash max-only exception note), advanced/statusline x4 (both 1M table entries), multi-llm/cg-mode x4. glm-5.3 documented as still-selectable everywhere it appears as the former default. Pricing sentences untouched (out of scope). 4-locale parity verified by grep. card: t289 🗿 MoAI * test(SPEC-GLM-FLASH-DEFAULT-001): M6 boot smoke TestGLMFlashDefaultEnvInjection Env-level smoke over the two injection surfaces (buildTmuxInjectVars map leg + setGLMEnv process-env leg with t.Setenv-registered restore per the R1 audit note): default configuration resolves glm-5.3-flash into all four ANTHROPIC_DEFAULT_*_MODEL slots and sizes MOAI_STATUSLINE_CONTEXT_SIZE / CLAUDE_CODE_AUTO_COMPACT_WINDOW at 1000000. No live z.ai API dependency. make build exit 0; catalog.yaml byte-unchanged; template-neutrality clean. card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): run-phase evidence — §E.2 tables + §E.3 audit-ready signal 13/13 AC PASS with verbatim command/output attribution; RED evidence pinned per milestone; coverage, boundary, lint, cross-platform build recorded. AC-001 set quote added to TestDefaultGLMConstants (slices.Equal + t.Logf). card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): sync-phase artifacts + 3-phase close (card t289) 🗿 MoAI * chore(SPEC-GLM-FLASH-DEFAULT-001): backfill sync_commit_sha (card t289) 🗿 MoAI * docs(t289): sync-audit verdict PASS 0.927 + F1/F2 fixups (card t289) - audit report committed (.moai/reports/t289/sync-audit.md) - F2: total_run_phase_files 33 -> 44 (audit recount) - F1: per-slot mixed llm.yaml residual recorded in §E.3 (reachable via web selects; consistent high-slot keying; out of scope) 🗿 MoAI * fix(graph): restamp codemaps provenance — 3-layer fresh after main advance (card t289) - described-source-diff 46 -> 0 (stamp re-anchored at a995e58fa which contains main da791eb0a); mx-index 0; edges 0; graph check exit 0 - recipe per t279 restamp 52f7ba135; provenance-only commit, graph/ output stays untracked per precedent 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 12 天前 | |
feat(t289): GLM-5.3-Flash default coding model — SPEC-GLM-FLASH-DEFAULT-001 (max-only reasoning, 1M context) (#1668) * fix(SPEC-GLM-FLASH-DEFAULT-001): plan-audit iter-1 defects D1-D7 applied (card t289) 🤖 Generated with [Claude Code](https://claude.com/claude-code) 🗿 MoAI * chore(SPEC-GLM-FLASH-DEFAULT-001): kickoff gate record + Phase 4 mode selection log + iter-2 verdict line (card t289) 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M1 register glm-5.3-flash and switch tier-slot defaults Add DefaultGLM53Flash / DefaultGLM53 named constants; retarget all seven tier-slot constants (High/Medium/Low/Fable + legacy Haiku/Sonnet/Opus) to glm-5.3-flash; ValidGLMModels() offers the 5-model set with glm-5.3 retained as an explicit member (derived-set drop guard). Template llm.yaml twin slots switched. Web i18n option labels added x4 locales (schema-parity guards require them once the set offers flash). Coupled tests updated. card: t289 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M2 flash max-only effort overlay branch Model-aware overlay: IsGLMFlashModel + CollapseClaudeEffortToGLMForModel / ResolveGLMReasoningForModel / SessionGLMReasoningStateForModel return the max state for EVERY Claude effort (incl. low) when the resolved model is glm-5.3-flash — flash accepts reasoning_effort: max only; non-flash collapse unchanged (low→low, above→max, unrecognized→max). Wire path threaded (resolveGLMBackendForLaunch returns the high slot; buildEnvForGLMLaunch / glmReasoningEnvVarsForModel) and both display surfaces (agentfm chip, model profile report) use the same model-aware resolution so display and wire agree. Template llm.yaml overlay comment documents the flash rule (documentation twin). setGLMEnv needs no threading: its session default is max under every model. card: t289 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M3 explicit glm-5.3-flash context window entry Add "glm-5.3-flash": 1_000_000 to glmContextWindows as an explicit entry (divergence guard) rather than relying on the glm-5.3 longest-substring match; registration-time guidance comment added. Table tests pin flash→1M direct, glm-5.3→1M retained, and an unregistered glm-5.3-* variant inheriting 1M via substring. card: t289 🗿 MoAI * test(SPEC-GLM-FLASH-DEFAULT-001): M4 flash web-label count pin (8 = 2 key families x 4 locales) The flash option labels (i18n.js) and the 5-model tier-set assertion landed with M1 — the schema-parity guards (TestDataI18nKeysSubsetOfDictionary / TestI18nKeySetParity) fail the moment the derived set offers flash, so a green M1 commit required them. This M4 commit adds the AC-010 mechanical pin: exactly 8 non-empty flash labels across f.llm.glm.models.opt.* and f.workflow.audit.glm.model.opt.* in en/ko/ja/zh. card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): M5 name glm-5.3-flash as default in README + docs-site (4-locale) Grep-gated to the surfaces that name the default model or enumerate the GLM set: README x4 (model enumeration + tier table + selectability note), docs-site multi-llm/_index x4, advanced/config-sections x4 (yaml example), advanced/profile-matrix x4 (fable binding + flash max-only exception note), advanced/statusline x4 (both 1M table entries), multi-llm/cg-mode x4. glm-5.3 documented as still-selectable everywhere it appears as the former default. Pricing sentences untouched (out of scope). 4-locale parity verified by grep. card: t289 🗿 MoAI * test(SPEC-GLM-FLASH-DEFAULT-001): M6 boot smoke TestGLMFlashDefaultEnvInjection Env-level smoke over the two injection surfaces (buildTmuxInjectVars map leg + setGLMEnv process-env leg with t.Setenv-registered restore per the R1 audit note): default configuration resolves glm-5.3-flash into all four ANTHROPIC_DEFAULT_*_MODEL slots and sizes MOAI_STATUSLINE_CONTEXT_SIZE / CLAUDE_CODE_AUTO_COMPACT_WINDOW at 1000000. No live z.ai API dependency. make build exit 0; catalog.yaml byte-unchanged; template-neutrality clean. card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): run-phase evidence — §E.2 tables + §E.3 audit-ready signal 13/13 AC PASS with verbatim command/output attribution; RED evidence pinned per milestone; coverage, boundary, lint, cross-platform build recorded. AC-001 set quote added to TestDefaultGLMConstants (slices.Equal + t.Logf). card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): sync-phase artifacts + 3-phase close (card t289) 🗿 MoAI * chore(SPEC-GLM-FLASH-DEFAULT-001): backfill sync_commit_sha (card t289) 🗿 MoAI * docs(t289): sync-audit verdict PASS 0.927 + F1/F2 fixups (card t289) - audit report committed (.moai/reports/t289/sync-audit.md) - F2: total_run_phase_files 33 -> 44 (audit recount) - F1: per-slot mixed llm.yaml residual recorded in §E.3 (reachable via web selects; consistent high-slot keying; out of scope) 🗿 MoAI * fix(graph): restamp codemaps provenance — 3-layer fresh after main advance (card t289) - described-source-diff 46 -> 0 (stamp re-anchored at a995e58fa which contains main da791eb0a); mx-index 0; edges 0; graph check exit 0 - recipe per t279 restamp 52f7ba135; provenance-only commit, graph/ output stays untracked per precedent 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 12 天前 | |
feat(t289): GLM-5.3-Flash default coding model — SPEC-GLM-FLASH-DEFAULT-001 (max-only reasoning, 1M context) (#1668) * fix(SPEC-GLM-FLASH-DEFAULT-001): plan-audit iter-1 defects D1-D7 applied (card t289) 🤖 Generated with [Claude Code](https://claude.com/claude-code) 🗿 MoAI * chore(SPEC-GLM-FLASH-DEFAULT-001): kickoff gate record + Phase 4 mode selection log + iter-2 verdict line (card t289) 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M1 register glm-5.3-flash and switch tier-slot defaults Add DefaultGLM53Flash / DefaultGLM53 named constants; retarget all seven tier-slot constants (High/Medium/Low/Fable + legacy Haiku/Sonnet/Opus) to glm-5.3-flash; ValidGLMModels() offers the 5-model set with glm-5.3 retained as an explicit member (derived-set drop guard). Template llm.yaml twin slots switched. Web i18n option labels added x4 locales (schema-parity guards require them once the set offers flash). Coupled tests updated. card: t289 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M2 flash max-only effort overlay branch Model-aware overlay: IsGLMFlashModel + CollapseClaudeEffortToGLMForModel / ResolveGLMReasoningForModel / SessionGLMReasoningStateForModel return the max state for EVERY Claude effort (incl. low) when the resolved model is glm-5.3-flash — flash accepts reasoning_effort: max only; non-flash collapse unchanged (low→low, above→max, unrecognized→max). Wire path threaded (resolveGLMBackendForLaunch returns the high slot; buildEnvForGLMLaunch / glmReasoningEnvVarsForModel) and both display surfaces (agentfm chip, model profile report) use the same model-aware resolution so display and wire agree. Template llm.yaml overlay comment documents the flash rule (documentation twin). setGLMEnv needs no threading: its session default is max under every model. card: t289 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M3 explicit glm-5.3-flash context window entry Add "glm-5.3-flash": 1_000_000 to glmContextWindows as an explicit entry (divergence guard) rather than relying on the glm-5.3 longest-substring match; registration-time guidance comment added. Table tests pin flash→1M direct, glm-5.3→1M retained, and an unregistered glm-5.3-* variant inheriting 1M via substring. card: t289 🗿 MoAI * test(SPEC-GLM-FLASH-DEFAULT-001): M4 flash web-label count pin (8 = 2 key families x 4 locales) The flash option labels (i18n.js) and the 5-model tier-set assertion landed with M1 — the schema-parity guards (TestDataI18nKeysSubsetOfDictionary / TestI18nKeySetParity) fail the moment the derived set offers flash, so a green M1 commit required them. This M4 commit adds the AC-010 mechanical pin: exactly 8 non-empty flash labels across f.llm.glm.models.opt.* and f.workflow.audit.glm.model.opt.* in en/ko/ja/zh. card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): M5 name glm-5.3-flash as default in README + docs-site (4-locale) Grep-gated to the surfaces that name the default model or enumerate the GLM set: README x4 (model enumeration + tier table + selectability note), docs-site multi-llm/_index x4, advanced/config-sections x4 (yaml example), advanced/profile-matrix x4 (fable binding + flash max-only exception note), advanced/statusline x4 (both 1M table entries), multi-llm/cg-mode x4. glm-5.3 documented as still-selectable everywhere it appears as the former default. Pricing sentences untouched (out of scope). 4-locale parity verified by grep. card: t289 🗿 MoAI * test(SPEC-GLM-FLASH-DEFAULT-001): M6 boot smoke TestGLMFlashDefaultEnvInjection Env-level smoke over the two injection surfaces (buildTmuxInjectVars map leg + setGLMEnv process-env leg with t.Setenv-registered restore per the R1 audit note): default configuration resolves glm-5.3-flash into all four ANTHROPIC_DEFAULT_*_MODEL slots and sizes MOAI_STATUSLINE_CONTEXT_SIZE / CLAUDE_CODE_AUTO_COMPACT_WINDOW at 1000000. No live z.ai API dependency. make build exit 0; catalog.yaml byte-unchanged; template-neutrality clean. card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): run-phase evidence — §E.2 tables + §E.3 audit-ready signal 13/13 AC PASS with verbatim command/output attribution; RED evidence pinned per milestone; coverage, boundary, lint, cross-platform build recorded. AC-001 set quote added to TestDefaultGLMConstants (slices.Equal + t.Logf). card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): sync-phase artifacts + 3-phase close (card t289) 🗿 MoAI * chore(SPEC-GLM-FLASH-DEFAULT-001): backfill sync_commit_sha (card t289) 🗿 MoAI * docs(t289): sync-audit verdict PASS 0.927 + F1/F2 fixups (card t289) - audit report committed (.moai/reports/t289/sync-audit.md) - F2: total_run_phase_files 33 -> 44 (audit recount) - F1: per-slot mixed llm.yaml residual recorded in §E.3 (reachable via web selects; consistent high-slot keying; out of scope) 🗿 MoAI * fix(graph): restamp codemaps provenance — 3-layer fresh after main advance (card t289) - described-source-diff 46 -> 0 (stamp re-anchored at a995e58fa which contains main da791eb0a); mx-index 0; edges 0; graph check exit 0 - recipe per t279 restamp 52f7ba135; provenance-only commit, graph/ output stays untracked per precedent 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 12 天前 | |
feat(t289): GLM-5.3-Flash default coding model — SPEC-GLM-FLASH-DEFAULT-001 (max-only reasoning, 1M context) (#1668) * fix(SPEC-GLM-FLASH-DEFAULT-001): plan-audit iter-1 defects D1-D7 applied (card t289) 🤖 Generated with [Claude Code](https://claude.com/claude-code) 🗿 MoAI * chore(SPEC-GLM-FLASH-DEFAULT-001): kickoff gate record + Phase 4 mode selection log + iter-2 verdict line (card t289) 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M1 register glm-5.3-flash and switch tier-slot defaults Add DefaultGLM53Flash / DefaultGLM53 named constants; retarget all seven tier-slot constants (High/Medium/Low/Fable + legacy Haiku/Sonnet/Opus) to glm-5.3-flash; ValidGLMModels() offers the 5-model set with glm-5.3 retained as an explicit member (derived-set drop guard). Template llm.yaml twin slots switched. Web i18n option labels added x4 locales (schema-parity guards require them once the set offers flash). Coupled tests updated. card: t289 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M2 flash max-only effort overlay branch Model-aware overlay: IsGLMFlashModel + CollapseClaudeEffortToGLMForModel / ResolveGLMReasoningForModel / SessionGLMReasoningStateForModel return the max state for EVERY Claude effort (incl. low) when the resolved model is glm-5.3-flash — flash accepts reasoning_effort: max only; non-flash collapse unchanged (low→low, above→max, unrecognized→max). Wire path threaded (resolveGLMBackendForLaunch returns the high slot; buildEnvForGLMLaunch / glmReasoningEnvVarsForModel) and both display surfaces (agentfm chip, model profile report) use the same model-aware resolution so display and wire agree. Template llm.yaml overlay comment documents the flash rule (documentation twin). setGLMEnv needs no threading: its session default is max under every model. card: t289 🗿 MoAI * feat(SPEC-GLM-FLASH-DEFAULT-001): M3 explicit glm-5.3-flash context window entry Add "glm-5.3-flash": 1_000_000 to glmContextWindows as an explicit entry (divergence guard) rather than relying on the glm-5.3 longest-substring match; registration-time guidance comment added. Table tests pin flash→1M direct, glm-5.3→1M retained, and an unregistered glm-5.3-* variant inheriting 1M via substring. card: t289 🗿 MoAI * test(SPEC-GLM-FLASH-DEFAULT-001): M4 flash web-label count pin (8 = 2 key families x 4 locales) The flash option labels (i18n.js) and the 5-model tier-set assertion landed with M1 — the schema-parity guards (TestDataI18nKeysSubsetOfDictionary / TestI18nKeySetParity) fail the moment the derived set offers flash, so a green M1 commit required them. This M4 commit adds the AC-010 mechanical pin: exactly 8 non-empty flash labels across f.llm.glm.models.opt.* and f.workflow.audit.glm.model.opt.* in en/ko/ja/zh. card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): M5 name glm-5.3-flash as default in README + docs-site (4-locale) Grep-gated to the surfaces that name the default model or enumerate the GLM set: README x4 (model enumeration + tier table + selectability note), docs-site multi-llm/_index x4, advanced/config-sections x4 (yaml example), advanced/profile-matrix x4 (fable binding + flash max-only exception note), advanced/statusline x4 (both 1M table entries), multi-llm/cg-mode x4. glm-5.3 documented as still-selectable everywhere it appears as the former default. Pricing sentences untouched (out of scope). 4-locale parity verified by grep. card: t289 🗿 MoAI * test(SPEC-GLM-FLASH-DEFAULT-001): M6 boot smoke TestGLMFlashDefaultEnvInjection Env-level smoke over the two injection surfaces (buildTmuxInjectVars map leg + setGLMEnv process-env leg with t.Setenv-registered restore per the R1 audit note): default configuration resolves glm-5.3-flash into all four ANTHROPIC_DEFAULT_*_MODEL slots and sizes MOAI_STATUSLINE_CONTEXT_SIZE / CLAUDE_CODE_AUTO_COMPACT_WINDOW at 1000000. No live z.ai API dependency. make build exit 0; catalog.yaml byte-unchanged; template-neutrality clean. card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): run-phase evidence — §E.2 tables + §E.3 audit-ready signal 13/13 AC PASS with verbatim command/output attribution; RED evidence pinned per milestone; coverage, boundary, lint, cross-platform build recorded. AC-001 set quote added to TestDefaultGLMConstants (slices.Equal + t.Logf). card: t289 🗿 MoAI * docs(SPEC-GLM-FLASH-DEFAULT-001): sync-phase artifacts + 3-phase close (card t289) 🗿 MoAI * chore(SPEC-GLM-FLASH-DEFAULT-001): backfill sync_commit_sha (card t289) 🗿 MoAI * docs(t289): sync-audit verdict PASS 0.927 + F1/F2 fixups (card t289) - audit report committed (.moai/reports/t289/sync-audit.md) - F2: total_run_phase_files 33 -> 44 (audit recount) - F1: per-slot mixed llm.yaml residual recorded in §E.3 (reachable via web selects; consistent high-slot keying; out of scope) 🗿 MoAI * fix(graph): restamp codemaps provenance — 3-layer fresh after main advance (card t289) - described-source-diff 46 -> 0 (stamp re-anchored at a995e58fa which contains main da791eb0a); mx-index 0; edges 0; graph check exit 0 - recipe per t279 restamp 52f7ba135; provenance-only commit, graph/ output stays untracked per precedent 🗿 MoAI --------- Co-authored-by: t <t@t.t> | 12 天前 | |
docs: embedded.go stale 참조 정정 — //go:embed all:templates 직접 임베드 truth 반영 내부 개발 문서 7개 파일에서 존재하지 않는 생성 파일 `internal/template/embedded.go`를 참조하던 오래된 서술을 실측 ground truth로 정정. 실제로는 `internal/template/embed.go`가 `//go:embed all:templates` + `//go:embed catalog.yaml` 디렉티브로 `templates/` FS를 빌드 시점에 바이너리로 직접 임베드하며, 생성되는 `.go` 파일은 없다. `make build`는 `gen-catalog-hashes --all` + `go build`로 바이너리를 재컴파일할 뿐 embedded.go를 생성하지 않는다. 정정 파일 (7개): - CLAUDE.local.md (§11 verify step를 존재하지 않는 파일 ls 대신 go build 성공 체크로 변경) - internal/template/CLAUDE.md (embedded_mirror_test.go → rule_template_mirror_test.go 파일명도 함께 정정 — 존재하지 않는 테스트 파일) - REVIEW.md (Skip 리스트를 실제 생성 산출물 catalog.yaml로 재지정) - .moai/docs/agent-lint.md + internal/template/templates/.moai/docs/agent-lint.md (byte-identical 아님, 각각 독립 정정) - .claude/agents/harness/cli-template-specialist.md - .claude/skills/harness-moaiadk-patterns/SKILL.md Template-First 규칙 자체는 보존 (임베드 메커니즘 서술만 정정). codemaps/reports/marketing/완료 SPEC는 제외 (auto-generated / immutable). 템플릿 중립성 유지 (§25: SPEC-ID/date/SHA/internal 토큰 없음). 검증: go build ./... exit 0, go test ./internal/template/... PASS, make build 성공(catalog.yaml 무변경), embedded.go grep 잔존 7건 전부 negation truth-context, embedded_mirror_test grep 0건. 🗿 MoAI | 2 个月前 | |
feat(github): add open-source AI automation infrastructure - Add CI workflow (Go test/lint/build matrix, Go 1.25) - Add release workflow (GoReleaser, Homebrew tap, cross-platform) - Add CodeQL security scanning (weekly + on push) - Add Claude Code GitHub Action (@claude mentions in issues/PRs) - Add community automation (welcome bot, stale bot, auto-labeler) - Add CodeRabbit config with multi-language and AI review rules - Add Dependabot config with grouped updates - Add issue templates (bug, feature, question) with multi-language support - Add PR template with AI collaboration checklist - Add GoReleaser config with Homebrew tap generation - Add label definitions (23 labels, 6 categories) - Add LICENSE (GPL v3) and SECURITY.md - Add FUNDING.yml for GitHub Sponsors Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> | 6 个月前 | |
chore(deps): bump the gomod-minor-patch group with 3 updates (#1630) Bumps the gomod-minor-patch group with 3 updates: [charm.land/bubbletea/v2](https://github.com/charmbracelet/bubbletea), [github.com/mattn/go-runewidth](https://github.com/mattn/go-runewidth) and [github.com/stretchr/testify](https://github.com/stretchr/testify). Updates `charm.land/bubbletea/v2` from 2.0.8 to 2.0.9 - [Release notes](https://github.com/charmbracelet/bubbletea/releases) - [Commits](https://github.com/charmbracelet/bubbletea/compare/v2.0.8...v2.0.9) Updates `github.com/mattn/go-runewidth` from 0.0.27 to 0.0.28 - [Commits](https://github.com/mattn/go-runewidth/compare/v0.0.27...v0.0.28) Updates `github.com/stretchr/testify` from 1.11.1 to 1.12.1 - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.11.1...v1.12.1) --- updated-dependencies: - dependency-name: charm.land/bubbletea/v2 dependency-version: 2.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gomod-minor-patch - dependency-name: github.com/mattn/go-runewidth dependency-version: 0.0.28 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gomod-minor-patch - dependency-name: github.com/stretchr/testify dependency-version: 1.12.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> | 14 天前 | |
chore(deps): bump the gomod-minor-patch group with 3 updates (#1630) Bumps the gomod-minor-patch group with 3 updates: [charm.land/bubbletea/v2](https://github.com/charmbracelet/bubbletea), [github.com/mattn/go-runewidth](https://github.com/mattn/go-runewidth) and [github.com/stretchr/testify](https://github.com/stretchr/testify). Updates `charm.land/bubbletea/v2` from 2.0.8 to 2.0.9 - [Release notes](https://github.com/charmbracelet/bubbletea/releases) - [Commits](https://github.com/charmbracelet/bubbletea/compare/v2.0.8...v2.0.9) Updates `github.com/mattn/go-runewidth` from 0.0.27 to 0.0.28 - [Commits](https://github.com/mattn/go-runewidth/compare/v0.0.27...v0.0.28) Updates `github.com/stretchr/testify` from 1.11.1 to 1.12.1 - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.11.1...v1.12.1) --- updated-dependencies: - dependency-name: charm.land/bubbletea/v2 dependency-version: 2.0.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gomod-minor-patch - dependency-name: github.com/mattn/go-runewidth dependency-version: 0.0.28 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gomod-minor-patch - dependency-name: github.com/stretchr/testify dependency-version: 1.12.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-minor-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> | 14 天前 | |
fix(install): ship install.bat with CRLF line endings for cmd.exe The blanket '* text=auto eol=lf' gitattributes rule left install.bat stored and distributed as LF-only bytes. cmd.exe requires CRLF to parse multi-line parenthesized blocks reliably: on the first end-to-end CI run (run 31960480175) the LF-only copy made the parser desync through the whole script — the arg-parsing block executed tokens as commands, the banner lines failed as unrecognized commands, the version-resolution block was skipped entirely, and the literal !VERSION! reached the download URL. The defect predates this card; the new end-to-end step is what surfaced it. Commit the CRLF bytes verbatim and disable normalization for *.bat ('-text') so every distribution path — clone checkout, CI checkout, raw.githubusercontent.com — receives CRLF ('text eol=crlf' would keep the index LF and leave raw-URL downloads broken). The version-resolution payload is byte-identical after conversion (cmp, 589 bytes). Card: t53 (fixup). 🗿 MoAI | 22 天前 | |
fix(install): resolve the latest version in install.ps1 without the REST API (#1567) #1559 moved the two shell installers off `GET /releases` and onto the `/releases/latest` redirect. The two PowerShell installers were not part of that change and still carry the logic it removed: $response = Invoke-RestMethod -Uri ".../repos/modu-ai/moai-adk/releases" $goRelease = $response | Where-Object { $_.tag_name -like "v*" ... } | Select-Object -First 1 Same two failures #1559 documented, unchanged on Windows: Rate limiting. Unauthenticated api.github.com allows 60 requests per hour per IP, and past that the endpoint answers with a JSON error object rather than an array. `Where-Object` finds no `tag_name` in it, so the installer reports "No releases found" and tells the user the project has never shipped. Reproduced by feeding the observed rate-limit response shape to the old filter: OLD RESULT: <No releases found> -> exit 1 Wrong endpoint. `/releases` returns releases in creation order, so the first entry is the most recently *created* release rather than the latest stable one. The port follows install.sh rather than re-deriving a design, so both installers resolve a version the same way: HEAD `/releases/latest`, read the resolved URI, take the segment after `/tag/`, strip a `go-` then a `v` prefix, and fall through to one shared failure message when nothing parses. Timeouts are bounded for the same reason the shell version bounds them. One thing the shell version does not have to deal with: the resolved URI lives in a different property per PowerShell edition. Windows PowerShell 5.1 returns System.Net.HttpWebResponse (ResponseUri); PowerShell 6+ returns System.Net.Http.HttpResponseMessage (RequestMessage.RequestUri). The script's header declares 5.1 support and its platform detection already branches on `$IsWindows` being null, so both are probed rather than assumed. Verified with PowerShell 7.5.4 against the live repository: NEW RESULT: 3.1.0 parse check: 0 errors The 5.1 branch is written from the documented response type but is not executed here — no Windows PowerShell on this machine. install.bat carries the same defect at line 58, where it shells out to the identical one-liner. It is left alone: cmd.exe quoting cannot be verified from this machine, and an unverified edit to a Windows entry point is worse than the bug it would fix. 🗿 MoAI Co-authored-by: t <t@t.t> | 23 天前 | |
fix(install): resolve the latest version without the rate-limited REST API (#1559) * fix(install): resolve the latest version without the rate-limited REST API The installer asked api.github.com for the release list and took the first entry. Two failures came out of that, and both were observed on the v3.1.0 release rather than reasoned about. Rate limiting. Unauthenticated api.github.com allows 60 requests per hour per IP. Behind a shared address — an office, a CI runner, a developer who has been driving `gh` — that budget is routinely spent, and the API then answers with a JSON error object carrying no tag_name. The old code read the empty result as "no releases exist" and told the user the project had never shipped, minutes after v3.1.0 went out with all six platform archives attached. Wrong endpoint. /releases returns releases in creation order, so the first entry is the most recently *created* one — a draft or a release candidate if either exists. A stale untagged draft holding v2.2.0 binaries was sitting in this repository's release list while that code was live. Both are fixed by resolving the /releases/latest redirect instead. It is not part of the REST API and is not rate limited, and GitHub excludes drafts and prereleases from it, so it means "latest stable" rather than "newest row". Verified against the failing condition rather than a healthy one: run with the IP's API quota already exhausted (remaining=0), the rewritten function reports `Latest Go edition version: 3.1.0`, where the old one exits with "No releases found". The error path is corrected too. A failure here now says GitHub could not be reached and offers a pinned-version install, rather than asserting that no release exists — a claim the installer was never in a position to make. Both copies move together: install.sh and docs-site/static/install.sh were byte-identical before this change and remain so. The docs-site copy is what https://adk.mo.ai.kr/install.sh serves. * fix(install): bound the version lookup and print a recovery command that works Two review findings, both correct. The recovery command was wrong. The error path printed `$0 3.1.0`, but the parser accepts only `--version VERSION` — a positional argument falls through to the unknown-option branch and exits 1. So the one instruction shown to a user whose version lookup had just failed was itself guaranteed to fail. The lookup was unbounded. It reads a single redirect header, so there is no reason for it to hang; without a bound, a stalled connection is indistinguishable to the user from an installer that has crashed. curl now carries --connect-timeout 5 --max-time 10, wget --timeout=5 --tries=1. The archive and checksum downloads are also unbounded. They are left alone here: they transfer 13 MB and need a much larger ceiling than a header read, and they are not what this change touches. Verified end to end with the corrected form rather than by reading it: $ bash install.sh --version 3.1.0 --install-dir <tmp> [SUCCESS] Installation complete! $ <tmp>/moai version moai-adk 3.1.0 * fix(install): parse the wget redirect header case- and CRLF-insensitively The wget branch matched on the literal prefix " Location: ", which binds three things that are not contractual: the exact two-space indentation of wget's -S output, the capitalisation of the header name, and the absence of a carriage return. HTTP header names are case-insensitive, and a CRLF response leaves a trailing \r glued to the URL — which would have travelled into the version string and then into a download path. Matched on the field instead: tolower($1) == "location:", with a trailing \r stripped from the value. This is the fallback branch, taken when curl is absent — a minimal Linux image is exactly where it runs and exactly where an unusual header rendering is most likely. Verified against each variant rather than by reading: ' Location: https://x/tag/v9.9.9' -> https://x/tag/v9.9.9 'location: https://x/tag/v9.9.9' -> https://x/tag/v9.9.9 ' LOCATION: ...v9.9.9\r' -> ...v9.9.9 (od -c confirms no \r) two Location lines -> the last one And end to end: `bash install.sh --version 3.1.0 --install-dir <tmp>` still completes. --------- Co-authored-by: t <t@t.t> | 23 天前 | |
chore(ci): SPEC-V3R4-CI-FASTTRACK-001 — CI fast-track (paths-filter + review bot consolidation) (#970) * chore(research): T4 audit claude.yml + review-quality-gate.yml (codex-independent confirmed) (SPEC-V3R4-CI-FASTTRACK-001) * chore(ci): T1 ci.yml paths-filter detect-job + conditional test matrix + skip-marker (SPEC-V3R4-CI-FASTTRACK-001) * chore(ci): T2 codeql.yml paths-filter + skip-marker (SPEC-V3R4-CI-FASTTRACK-001) * chore(ci): T3 delete 5 review workflows (codex/gemini/glm/llm-panel/optional) (SPEC-V3R4-CI-FASTTRACK-001) * chore(tooling): T5 lefthook.yml + Makefile preflight target (SPEC-V3R4-CI-FASTTRACK-001) * chore(ci): T6 release-pr-multi-os.yml — release branch PR full matrix (revised per user directive 2026-05-17) (SPEC-V3R4-CI-FASTTRACK-001) * docs(local): T7 CLAUDE.local.md §18.7 정정 — 4 required checks + 3-tier philosophy (SPEC-V3R4-CI-FASTTRACK-001) * chore(ci): evaluator-active fix-pack — EC-1 mutual exclusivity + lefthook install ref + AC-CIFT-008 lessons #19 (SPEC-V3R4-CI-FASTTRACK-001) Addresses evaluator-active FAIL verdict (Score 72.75) with 3 fixes: - P2 EC-1 mutual exclusivity: ci.yml + codeql.yml skip-marker if-guard 에 `&& go_code != 'true'` 추가 → 혼합 PR (docs + Go) 에서 real test + skip-marker 중복 실행 방지. EC-1 acceptance.md 명세 동작 보장. - P1 AC-CIFT-005 lefthook install ref: CLAUDE.local.md §18.7 에 "Local pre-push verification (lefthook)" subsection 추가 (brew install lefthook + lefthook install + LEFTHOOK=0 bypass). - P1 AC-CIFT-008 lessons #19: acceptance.md AC-CIFT-008 verification 명령을 `#18` → `#19` 로 정정 + note 추가 (#18 = race prevention captured mid-session, #19 = CI 3-tier). spec.md/plan.md #18 references 는 sync-phase 에서 처리. Out of scope (sync-phase 처리): - spec.md L103/L134, plan.md L540/L547/L552 의 #18 references (정보 reference, verification gate 영향 없음) - T6 release-PR trigger drift (REQ-CIFT-006/AD-005/AC-CIFT-006/scenarios 5) 🗿 MoAI <email@mo.ai.kr> | 3 个月前 |
MoAI-ADK
验证驱动的智能体编排框架 —— 让 Claude Code 写出的代码值得信赖的结构
官方文档 · 图书:用 Claude Code 开始实战智能体编程 · Discord
“模型是一个逐个 token 前进的概率型工作者。它无法逐轮记住这轮该花多少、做得好不好、上个会话中断在哪里。框架(harness)从外部把这三件事都强制住。”
v3.1 新功能 —— 看板模式
v3.1 在 8 月 15 日(韩国光复节)发布。意图是让工作从“单个会话被上下文上限捆住”的旧形态中解放出来。上限本身并不会消失 —— 真正改变的点都如实写在下面。
一个会话占用一个上下文窗口。长 SPEC 会填满这个窗口,后面的工作背着前面的一切前进:已经结束的计划在评审时仍留在窗口里,评审在写文档时又还留着。常见的逃生口 /clear 会把来龙去脉连同负担一起扔掉。
看板模式把一项工作从一个终端拆到四个终端。主控会话(lead)驾驶整条链,三个伴随会话各认领 plan、run、sync 中的一列,只背自己那一列的上下文。评审不是单独的列,而是由 sync 门禁吸收 —— sync 阶段亲自运行评审视角得出结论。这不是解除上限 —— 每个会话的限额原样存在。改变的是:任何会话都不再背着三个阶段的历史,于是同样的预算能走得更远,结束的阶段可以清空而不丢失卡片。
每一列都可以用不同的后端和推理强度。上面的画面把 Plan 跑在 Opus 5 high、Run 跑在 GLM 5.2 xhigh、Sync 跑在 GLM 5.2 上 —— 因为每一列需要的推理深度并不相同。
开始使用
moai cc -k # 主控 —— 告知 run-id 并铺设链条
moai cc -k --name plan # 伴随会话,各自在单独的终端里
moai cc -k --name run
moai cc -k --name sync
伴随会话要由人手在新的终端里逐个启动。名字只用角色名 —— run-id 是主控会话的标识符,伴随会话不携带它;同一个角色名已被占用的会话占着时,下一个会话顺次拿编号。会话不能替别的会话启动。任何一列把 moai cc 换成 moai glm,就只有那一列跑在 GLM 后端上。
后端怎么搭配
打开看板时,引导信息会一并给出默认推荐 —— 若优先考虑 token 可用性:主控用 moai glm -k,plan 用 moai cc -k --name plan,run 用 moai glm -k --name run,sync 用 moai cc -k --name sync。这样安排的理由是每条泳道需要的推理种类不同:plan 和 sync 是做判断和评审的列,交给 Claude;run 以实现为主,用 GLM 压低成本。主控不是下判定的位置,而是守着队列搬卡片的位置,适合常驻等待成本不高的 GLM。GLM 主控之下需要 Claude 判定时,会经名为 judge 的会话绕出去 —— 这是 GLM 主控使用 Claude 的唯一途径。一个账号开始被 429 限流时,把各条泳道分散到不同账号来安排是行之有效的做法。这个组合终究只是默认推荐 —— 换别的组合、或把全部会话统一到单一后端都没问题。
工厂模式 —— N 条泳道同时搬运多张卡片
-f 打开工厂主控,这是看板的第二种形态。看板里的卡片在各列之间跳,而工厂里的卡片整张进一条泳道,由那条泳道在会话内串行走完 plan → run → sync,每个阶段都以 Agent() 子智能体的形式启动。泳道名为 lane-1 … lane-N。
moai cc -f # 主控 —— 默认一条泳道 (lane-1)
moai cc -f 4 # 主控 —— 四条泳道
moai cc -f lane-1 # 一条泳道,在自己的终端里
moai glm -f lane-3 # ……GLM 后端上的一条泳道
用 moai cc -f lane-<n> 一条一条地加泳道。这种写法已经指定了泳道名,再给 --name/-n 会报错。只有活着的会话占用的编号才会被跳过 —— 泳道死了,编号就释放,可以再用。哪个编号被谁占着,记在 .moai/state/factory/workers.json 里,残留的占用也从这里清掉。一条泳道最多并发运行 10 个 Agent() 子智能体,其中承担写入的生成各自隔离在自己的工作树里。千万不要一次把所有泳道全开 —— 先起第一条,确认它真的开始产出,再激活其余。卡片绝不会被拆到多条泳道上。-k 依旧驱动三角色的看板链;一次启动只能带一个进入标记,所以 -k 与 -f 同时给出会报错,moai cg 也拒绝工厂模式。
详见:看板模式 —— 工厂模式
看板是 backlog → plan → run → sync → done 五列。backlog 刻意不设归属会话 —— 工作只有人放进去,才会进入看板。
/moai todo "rename 提示过时了" # 追加卡片
/moai todo # 查看队列
有两条规则让看板保持诚实。主控只凭自己从卡片 progress.md 里读到的证据推进卡片 —— 不凭伴随会话的回复,因为回复是主张而不是观测,而且跨会话投递并不保证送达。另外,一个阶段结束后,主控会请你手动 /clear 对应会话 —— /clear 是用户亲手敲的命令,无法当作指令发送。
四个会话共用的词汇
把看板文档里反复出现的词汇收进一张图。列 (column) 是看板的阶段;泳道 (lane) 是把一张卡片一路送到终点的“会话 + 工作树”组合 —— 区别就像站点和线路。
操作者 ── /moai todo ──▶ backlog ─▶ plan ─▶ run ─▶ sync ─▶ done
(主控只凭读到的证据把卡片送入下一列)
泳道 —— 卡片 t0: run 会话 + 工作树 t0 ┐ 两条流共用同一块看板,
泳道 —— 卡片 t1: run 会话 + 工作树 t1 ┘ 并排流动、互不混杂
| 词汇 | 一句话定义 |
|---|---|
| 卡片 (card) | 一个工作单元。经 /moai todo 进入,以短 ID 相称 |
| 列 (column) | 看板的一个阶段 —— 五列顺序固定 |
| 积压区 (backlog) | 入口等待队列。没有归属会话,只有人能投放 |
| 泳道 (lane) | 把一张卡片送到终点的“会话+工作树”组合。一条并行工作流 |
| 主控 (lead) | 协调者会话。只凭读到的证据推进卡片,自己不写代码 |
| 伴随会话 (companion) | 坐镇某一列干活的会话。由人手逐终端启动 |
| 运行 ID (run-id) | 主控启动时告知的短标识符。它是主控会话的名字,伴随会话不携带它 |
| 工作树 (worktree) | 卡片专用的隔离检出。目录名承载卡片 ID,分支名写清做了什么(WT-<slug>)。从 run 到 sync 一棵贯通 |
| 派单 (dispatch) | 主控发给伴随会话的指令 —— 是工作的指针,不是副本 |
带定义和示例的正式术语表:看板术语
卡片也会因形状不同而走不同的列。主控在卡片离开 backlog 时将其归入三个类别之一,并在派单文里写明。
| 类别 | 形状 | 捷径 |
|---|---|---|
| A —— 立即关闭 | 一个文件·一行,无设计判断,回归由 CI 捕捉 | 一个会话包办到 PR(跳过 plan) |
| B —— 原因未查明的缺陷 | 明显坏了,但原因还没确立 | run → sync(不经 plan,没有 SPEC) |
| C —— 设计变更 | 含有决策,或横跨多个子系统 | 三个列全走 |
类别 A 只凭查证过的证据认定,不接受主张 —— 引用不出以单文件测得的 diff 和将在合并的 HEAD 上跑绿的 CI,就不是类别 A。类别 B 只跳过 plan,sync 门禁的评审照常运转,并把原因确立的证据(复现命令与输出)留在卡片的进度记录里。
详见:看板模式 —— 卡片类别
用眼睛看板
moai web 会启动一个本地控制台。看板画面把看板链条和 SPEC 流水线放在一起,还附带 Overview、Specs、Monitor、Settings、Todo 画面。
详细指引:看板模式 · manager-lead 领导协调者 · /moai todo
v3.1.1 新增的部分
除了看板模式,v3.1.1 还带来了下面这些。每一项都在后面对应的章节里细讲。
主目录清理。 用得越久,~/.moai 里堆的历史运行产物就越多。moai clean --home 只在允许清单的范围内清理它们 —— 默认是 dry-run,先把要删的东西摆出来给你看,真删要加 --force。从几天前的东西开始清由 state.home_retention_days 决定(默认 30 天,填 0 就关掉)。眼下膨胀到多大,moai doctor 的 Home Disk Usage 项会告诉你。主目录本身可以用 MOAI_HOME 环境变量挪走 —— 只接受绝对路径。不过读这个变量的只有 Go 进程,路径挪了,状态栏和 shell 钩子照旧看 $HOME/.moai。.env.glm 这类 shell 侧的凭据和状态栏数据就留在原地,状态悄悄裂成两处。
跨会话消息设置。 别的 Claude Code 会话发来的消息,是直接收下、先经审批再收下、还是干脆挡掉,由 crosssession.yaml 决定。要求走出这台机器的消息必须先经审批的开关也在这里。
状态栏支持 GitLab。 用 statusline.forge 选择在 GitHub 还是 GitLab 上统计打开中的工作。留空就按 origin 远端的主机判断。
裸 /loop 变成看板工头。 不带参数只敲 /loop,转起来的是这样一个循环:盯着积压队列,把操作者已经标成 picked 的下一张卡片派给隔离的工人,完成与否不看主张、只看读到的证据,然后汇报。这是没人盯着的位置,所以往队列里放卡片和挑卡片都是操作者的事,工头不挑,只负责搬运。
为什么选择 moai-adk
智能体写代码的时代已经到来,但智能体交出的结果不能照单全收。“测试通过了”这句话到底是真跑过测试的结果,还是智能体的猜测 —— 从一开始,分辨这一点就是最大的问题。moai-adk 正是从这一点出发 —— 在系统层面禁止未经验证的完成声明,并把每个完成主张与实际执行的命令及其输出绑定为证据。
moai-adk 是从外部包裹 Claude Code 的框架(harness)。它不取代 Claude Code,而是用结构接管过去要你亲手照看的部分 —— 用哪个模型、推理多深、怎么验证结果、会话断了怎么接续、并行运行时怎么隔开才不互相踩踏。验证完整性、SPEC 生命周期、带真实边界的自主执行、活的代码库导航器、自我改进循环、并行安全结构。这六件事构成 moai-adk 的身份。
这份身份整理为三个核心 (three axes) —— 用更少的 token 拿到同样质量的成本(token 经济学)、把观测变成规则、越跑越聪明的自我改进(智能体循环工程),以及从结构上防止返工的质量管理(SPEC 生命周期 · TRUST 5 门禁 · 隔离)。单独哪一个都不够 —— 下面看它们为什么彼此需要。
八个差异点
| 差异点 | 说明 |
|---|---|
| 没有虚假验证 | “测试通过”的主张必须归因于实际执行的命令及其输出。系统禁止把没跑过的验证说成成功 —— 验证主张完整性(verification-claim integrity)绑定在每个智能体和编排器表面上。 |
| 自主 + 真实边界 | 用 /moai goal 声明完成条件,会话就会自主工作直到条件满足。同时绑着四道硬边界 —— 轮次上限(默认 30)、停滞守卫、墙钟预算、事前审批门 —— 不会掉进无限循环。 |
| 并行安全 | 每个 SPEC 独占一棵工作树,分支状态守卫拦住主检出里误切的分支,启动写入型智能体前先检查与远端的差距。两个可写智能体从不同时运行。 |
| 长程延续 | 工作跨过 /clear 存续。进度留在 progress.md,交接消息留在记忆,路由决策留在决策记忆。下一个会话从上一个学会的地方起步,而不是从零开始。 |
| 成本高效 | 按工作阶段和 SPEC 尺寸声明式地指派模型与推理深度。Claude 主控 + GLM 工人的 CG 模式在实现密集型工作上省 60–70% 成本。复用提示缓存、把长输出排到磁盘,保持上下文轻量。 |
| 16 种编程语言同等支持 | Go、Python、TypeScript、JavaScript、Rust、Java、Kotlin、C#、Ruby、PHP、Elixir、C++、Scala、R、Flutter、Swift —— 十六种编程语言作为一个集合,用基于标记的自动检测统一处理。没有任何一种受到优待。 |
| 自我改进 | 观测到反复出现的失败模式就上升为规则修改提案。绝不悄悄应用 —— 先审批再落地。路由决策和门禁证据沉淀进决策记忆,成为下一次运行的材料。 |
| 母语友好 | 韩语、日语、中文、英语四个语言区在同一 PR 内维护,禁止翻译腔,每种语言各有自己的母语行文。绝不强迫母语用户使用英语。 |
有什么不同
| Claude Code 单独 | 一般框架 | moai-adk | |
|---|---|---|---|
| 完成主张的证据归因 | 用户亲手核对 | 通常没有 | 系统强制(5 段证据报告格式) |
| SPEC 生命周期 | 无 | 有限 | plan→run→sync 三阶段 + Tier S/M/L |
| 自主循环的硬边界 | 不适用 | 多半只有轮次上限 | 轮次上限 + 停滞守卫 + 墙钟 + 审批门 |
| 并行工作隔离 | 手动 | 有限 | worktree + 分支守卫 + 启动前同步检查 |
| 会话延续性 | /clear 后中断 |
有限 | 交接 + 记忆 + 进度文件 |
| 16 种编程语言同等对待 | 不适用 | 不适用 | 标记自动检测 + 各语言工具链 |
| 自我改进循环 | 无 | 有限 | 失败观测 → 规则晋升(审批制) |
flowchart TD
User["用户请求"] --> Analyze["意图分析<br/>Analyze-First 路由"]
Analyze --> Plan["plan — 编写 SPEC"]
Plan --> Audit["独立审计<br/>plan-auditor"]
Audit --> Run["run — TDD/DDD 实现"]
Run --> Verify["trust-but-verify<br/>验证批处理"]
Verify --> Sync["sync — 文档 + PR"]
Sync --> Learn["决策记忆 + 教训"]
Learn -.下一个会话.-> Analyze
三个核心互相撑住
只压成本,质量会悄悄垮掉 —— 返工和调试循环随之而来,而返工是所有 token 支出里最贵的。只有质量门禁而没有学习循环,同样的错误每个会话重演一遍。没有成本上限的自主循环,一个失控任务就能烧光配额。三个核心互相撑住 —— 质量挡住返工,成本才保得住经济性;循环捕捉有效的做法,质量才始终可强制;成本门禁在超额前刹停,循环才留在付得起的范围内。
每一项设计决策都服务于这三个核心之一。用哪个模型、推理多深、上下文怎么花 —— 没有一件被丢给每轮的临场发挥。系统来决定,并把决定记录下来,让下一次运行更聪明。
成本由指派决定,不由单价决定
三年间 token 价格跌了 98%(Linux Foundation),同期企业 AI 支出却涨了 320%。用量增长把降价整个盖了过去。智能体为解决一个任务要转几十到几百步,token 按比例烧掉。在按量计费下这直接变成账单;在订阅制下,它蚕食所有模型共享的每周配额。
Uber 把 Claude Code 部署给 5,000 名工程师,四个月烧掉一年的编码预算,随后引入月度 token 限额。Meta、Amazon、Microsoft 也各自撤回了无限 AI 政策。把任务匹配给合适模型、提升 token 效率的 token 经济学成了科技行业的新基线。
传统成本控制是为单价上涨设计的,在这道悖论面前无能为力:价格在跌、总支出在涨。瓶颈不是单价而是用量 —— 更准确地说,是智能体在完成任务前转的步数。
DeepSWE 排行榜(113 项任务、按努力度分视图)证明了这一点。同一个 Claude 家族内部,单任务成本跟着模型多高效地完成走,而不是跟着 token 单价走。
| 模型 [effort] | 得分 | 单任务成本 | 备注 |
|---|---|---|---|
| opus-5 [low] | 58%±2 | $1.66 | |
| opus-5 [medium] | 69%±1 | $3.29 | 性价比拐点 |
| opus-5 [high] | 73%±2 | $6.08 | 得分 +4,成本 1.8 倍 |
| opus-5 [xhigh] | 73%±3 | $9.07 | 纯亏损 —— 与 high 持平,只多花 49% |
| opus-5 [max] | 74%±4 | $11.84 | |
| glm-5.2 [max] | 44%±2 | $3.92 | API 计费下吃亏 · z.ai 包月制下有用 |
| sonnet-5 [max] | 54%±4 | $26.40 | 被 opus-5 [low] 支配 |
Opus 5 用最低努力度跑,得分反而高于 Sonnet 5 用最高努力度(58% vs 54%),单任务成本只有十六分之一($1.66 vs $26.40)—— 尽管 Sonnet 的 token 单价更便宜。原因是 268 步对 36 步:写账单的是重试循环,不是 token 费率。成本由给每个任务指派合适的模型和推理深度决定。

来源:DeepSWE v1.1 排行榜(datacurve.ai,113 项任务,2026-07-25)
快速开始
安装
macOS / Linux / WSL
curl -fsSL https://adk.mo.ai.kr/install.sh | bash
Windows (PowerShell 7.x+)
irm https://adk.mo.ai.kr/install.ps1 | iex
从源码构建 (Go 1.26+)
git clone https://github.com/modu-ai/moai-adk.git
cd moai-adk && make build
已安装过?用 moai update 升到最新版本。从 v3.1.1 起,moai update 在清空并重铺模板管理目录之前,会先把那里面不受管理的文件挪到 .moai-backups/<时间戳>/pre-clean/。备份失败就当场停下,不会往删除那一步走 —— 你自己放进去的文件不会在重铺时被悄悄冲掉。
💡 想省成本 —— 推荐 z.ai GLM:通过这个链接注册 z.ai 可获得一定量的赠送 token。这个链接也是赞助 moai-adk 开源开发的途径。也有免费模型(GLM-4.7-Flash、GLM-4.5-Flash),参见 z.ai 定价。
初始化项目
moai init my-project
cd my-project
交互式向导自动检测语言、框架和方法论,选好模型策略后一直生成到 Claude Code 集成文件。
第一个工作流
claude # 或者 moai cc —— 在项目里运行 Claude Code
/moai plan "添加 JWT 登录" # 编写 SPEC
/moai run SPEC-AUTH-001 # TDD/DDD 实现
/moai sync SPEC-AUTH-001 # 文档同步 + 创建 PR
自然语言也可以。像 /moai "修一下登录 bug" 这样写,意图分析(Analyze-First 路由)会读出请求并转入合适的工作流。
环境要求
| 平台 | 支持环境 | 备注 |
|---|---|---|
| macOS | Terminal, iTerm2 | 完全支持 |
| Linux | Bash, Zsh | 完全支持 |
| Windows | 推荐 WSL,PowerShell 7.x+ | 原生 cmd.exe 不支持 |
- Git —— 所有平台必备
- Claude Code —— moai-adk 是为 Claude Code 准备的框架
- 建议:
ghCLI(PR 自动化)、tmux(CG 模式)、所用语言的 lint/测试工具链(如golangci-lint)
核心功能
单一入口 /moai
自然语言和 16 个子命令进入同一条流水线。/moai plan、/moai run、/moai sync 是 SPEC 流水线的主轴;goal、loop、fix、review、gate、clean、codemaps、e2e、mx、feedback、project、harness、todo 补齐四周。
已退役的 4 个子命令 ——
design·brain·coverage·security。security的职责由moai-ref-owasp-checklist+moai-ref-llm-security技能接手。
MCP 服务器
moai init 默认恰好准备一个启用的 MCP 条目 —— 自带的 moai mcp-server(本地 stdio 服务器)。它向 Claude Code 暴露分成六组的 21 个 MoAI 工具。四个已记载但未启用的条目(context7、chrome-devtools、playwright、ast-grep)用 moai mcp add <名称> 打开。moai mcp add|remove|list CLI 通过 atomic-RWM seam 管理条目,用户无需手改 .mcp.json。
| 组 | 工具 | 用途 |
|---|---|---|
| SPEC 生命周期 | spec_progress, spec_audit, spec_drift |
时代分类 + 漂移检测 |
| 验证 | verify_snapshot, verify_trend |
按键的证照快照 |
| 目标 + 会话 | goal_arm, goal_status, session_list |
自主循环 + 多会话协调 |
| 跨模型审计 | audit_multi, codex_audit, glm_audit, audit_cache |
多审计者收敛 |
| codex 委派 | codex_task, codex_setup, codex_job_* |
后台跨模型作业 |
| GLM 委派 | glm_task, glm_job_status, glm_job_result, glm_job_cancel |
GLM(z.ai)后台作业委派 |
所有后端都是 fail-open —— GLM(~/.moai/.env.glm)和 codex(~/.codex/auth.json)是可选的;不可用的后端返回 inconclusive,绝不是 hard error。
在双 harness(moai init --agent codex|both)下,Codex 的状态栏只支持内置标识符数组(tui.status_line),因此 goal、todo、SPEC 状态等 MoAI 专属条目无法显示 —— 这是在 openai/codex#17827 落地命令驱动的状态栏之前的已知限制。
详见:MCP 服务器指南 · Claude Code MCP
goal 引擎 —— 带真实边界的自主循环
声明完成条件,会话就自主工作直到条件满足。轮次上限、停滞守卫、墙钟预算、事前审批门一起绑着,掉不进无限循环。机械条件(命令退出码)和模型条件(对话记录里的主张)都能用。--max-turns 0 还能武装 auto-compact 驱动的无限 goal —— 此时由 --max-duration 和停滞守卫提供边界。
并行 worktree
每个 SPEC 独占一棵工作树。用 moai cc -w <名称> 进入;加 --spawn 则在保留当前会话的同时开新窗口。分支状态守卫拦住主检出里误切的分支。
看板模式
--kanban(短写 -k)是会话启动器开关,在主控会话的指挥下把单个 SPEC 沿 plan → run → sync 推进,并用多会话看板协调。看板的骨架是 Origin-Trail Chain —— 一棵 append-only 的 JSONL 谱系树,追踪 worktree 祖先、解决深度遗忘(/clear 之后从根到叶恢复链条)、并通过心跳陈旧度检出死掉的主控会话。
| 概念 | 作用 |
|---|---|
| Origin-Trail Chain | .moai/state/chain/events.jsonl 的 append-only JSONL 事件流 |
| WorktreeNode(13 字段) | 每会话状态:ID、父节点、深度、origin 链、里程碑、恢复目标 |
| CWD 冲突消解 | 用 (worktree_path, session_id) 对区分复用路径 |
| 深度上限 | 限制嵌套复杂度 |
现在就能用:
moai cc -k(或moai glm -k)启动主控,用-k --name <role>逐个接入伴随会话 —— 每终端一个,手动启动。moai chain <status|lineage|back|list|prune>读谱系,moai todo(不带参数查看队列,add·list·next·done·unpick·drop·undrop·edit·move·analyze,两个以上的词直接当作追加卡片)运营backlog列。启动顺序见上文“v3.1 新功能 —— 看板模式”一节。
详见:看板模式指南
CG 模式 —— Claude 主控 + GLM 工人
Claude 负责战略、计划和审计;GLM 扛大批量实现。两者通过 tmux 会话级环境隔离接起来,在实现密集型工作上省 60–70% 成本。
16 种编程语言同等支持
Go、Python、TypeScript、JavaScript、Rust、Java、Kotlin、C#、Ruby、PHP、Elixir、C++、Scala、R、Flutter、Swift。基于标记的自动检测驱动每种语言的标准 lint/格式化/测试工具链。
自动质量门禁
TRUST 5(Tested · Readable · Unified · Secured · Trackable)作用于每一次变更。/moai gate 一趟跑完 lint + 格式化 + 类型 + 测试,sync-auditor 按功能、安全、做工、一致性四个维度打分。
@MX 标签
让智能体之间交接上下文、不变量和危险区的行内代码标注。只给高扇入、复杂或危险的代码做标记。
Navigator —— 活的代码库地图
@NAV:DEC、@NAV:SYM、@MX:SPEC 三类 token 绑进一张可寻址的图(nav-graph.json)。设计决策、SPEC 和代码符号双向相连 —— 改代码时,决策的来龙去脉跟着一起到。
会话交接
工作跨过 /clear 存续。6 段式 paste-ready resume 消息把进度带到下一个会话;自动注入模式下,一条消息即可恢复会话。
loop / fix —— 错误驱动开发
/moai loop 并行扫过 LSP 诊断、AST-grep 和 linter,把抓到的问题按级别归组,直到队列清空。/moai fix 是一趟搞定的一次性修缮。
review --deep
/moai review --deep 运行多智能体对抗式漏洞扫描,背后跟着 OWASP · LLM 安全 · 供应链 · DevSecOps 参考技能。
四语言区文档
韩语、日语、中文、英语文档在同一 PR 内维护。禁止翻译腔,每种语言各有母语行文,四语言区一致性检查绑在构建门禁上。
moai web 控制台
moai web 打开一个只监听本地主机的控制台。画面共六个 —— Overview、Kanban、Specs、Monitor、Settings、Todo;设置画面分成十一个标签页:Identity、Language、LLM、3rd Party LLM、Workflow、Git & Worktree、Audit、Agents、Report、MCP、Cross-Session。档案的创建、改名、删除也在同一画面完成。
ref / domain 技能
ref 技能 11 个(moai-ref-api-patterns、moai-ref-owasp-checklist、moai-ref-llm-security、moai-ref-react-patterns、moai-ref-testing-pyramid、moai-ref-ui-polish、moai-ref-secops、moai-ref-supply-chain、moai-ref-seo、moai-ref-git-workflow、moai-ref-cross-model-audit)与 domain 技能 7 个(moai-domain-backend、moai-domain-frontend、moai-domain-database、moai-domain-design-dna、moai-domain-html-report、moai-domain-humanize、moai-domain-svg-infographic)向智能体注入现场知识。
SVG 技术信息图
moai-domain-svg-infographic 技能生成可编辑的 SVG 技术信息图。写标记之前先用数值算出坐标,完成的文件要通过对确定性源码 lint 和带尺寸校验的 2 倍分辨率 PNG 渲染。通过外部目录基准实测了九种形态——审批门流程、前后对比、KPI 卡片网格、决策矩阵、分层堆叠、嵌套作用域、流程图、路线图时间线、组件拓扑——确认九种全部可复现(分形态产物与判定:.moai/reports/t272/verdict.md)。
跨平台
一个无额外依赖的 Go 单一二进制,跑在 macOS、Linux、Windows 上。钩子系统机械地强制门禁,状态栏实时显示成本和上下文。
它是如何工作的
SPEC 三阶段生命周期
所有工作沿 plan → run → sync 三个阶段流动。Tier S/M/L 尺寸分级决定验证深度和 PR 路由。GEARS 格式的需求与验收标准以证据判定完成。
flowchart TD
P["plan — 编写 SPEC<br/>GEARS 需求 + 验收标准"] --> PA["plan-auditor<br/>独立审计(防偏)"]
PA -->|PASS| R["run — TDD / DDD 实现<br/>cycle_type 自动选择"]
PA -->|DEBT| P
R --> SA["sync-auditor<br/>4 维质量评分"]
SA -->|PASS| S["sync — 文档同步 + PR"]
SA -->|DEBT| R
S --> MX["@MX 标签 + Navigator 更新"]
方法论(TDD/DDD)由项目状态挑选。moai init 看覆盖率自动决定。
flowchart TD
A["项目分析"] --> B{"新项目或<br/>覆盖率 ≥10%?"}
B -->|"是"| C["TDD(默认)"]
B -->|"否"| D["DDD"]
C --> F["RED → GREEN → REFACTOR"]
D --> G["ANALYZE → PRESERVE → IMPROVE"]
| 方法论 | 循环 | 适用 |
|---|---|---|
| TDD(默认) | RED → GREEN → REFACTOR | 新项目、功能开发 |
| DDD | ANALYZE → PRESERVE → IMPROVE | 覆盖率低于 10% 的存量代码 |
12 智能体目录
| 分类 | 智能体 | 成本 | 职责 |
|---|---|---|---|
| 管理者 | manager-spec | 🔴 | plan 阶段编写 SPEC |
| manager-develop | 🔴 | run 阶段 TDD/DDD/autofix 实现 | |
| manager-docs | 🔵 | sync 阶段文档 | |
| manager-git | 🩵 | PR 创建与路由 | |
| manager-design | 🟠 | 设计阶段协作(Claude Design) | |
| manager-lead | 🔴 | 层级团队 Tier L 协调 + 看板·工厂领导会话派工(唯一的 Agent 携带者,深度 2 封印) | |
| 评审者 | plan-auditor | 🔴 | 独立 plan 审计(防偏) |
| sync-auditor | 🔴 | 4 维质量评分(功能性 40 · 安全 25 · 做工 20 · 一致性 15) | |
| 构建者 | builder-harness | 🟠 | 项目专用智能体、技能、命令、钩子的脚手架 |
| 顾问 | super-advisor | 🔵 | 按需高推理咨询(E1-E4 升级) |
| 专员 | e2e-tester | 🟠 | Web/移动/桌面 E2E 测试执行(CLI 优先) |
| 内置 | Explore | ⚪ | 只读代码库探查 |
成本颜色跟随默认 medium 档位的模型×推理单元(用 moai model profile 查看):🔴 opus+high · 🟠 opus+medium · 🔵 opus+low · 🩵 sonnet+low · ⚪ 继承会话模型(用户自加智能体)。切换档位(high/low)后指派随之变化。写作和审计从一开始就分给别人 —— 写的人永远不给自己的作业打分。
十二个里有十一个是 moai-adk 自造的智能体,Explore 是 Claude Code 本来就有的内置智能体。Explore 不另外持有自己的模型,而是原样继承会话模型,所以没有档案单元。目录是 12 个,而后面模型档案一节里的单元数是 11 × 3 = 33 —— 两个数字并不矛盾,它们数的是不同的东西。
trust-but-verify —— 给完成主张绑上证据
智能体报告“测试通过了”时,编排器不照单全收,而是亲自跑验证批。七个只读验证(测试、覆盖率、子智能体边界、哨兵扫描、CLI 冒烟、基准、lint)在一轮里并行执行,各自的退出码和输出留作证据。
验证主张完整性(verification-claim integrity)规则在背后托住这条流程 —— 不许把没跑过的验证说成成功、不许把以前量过的值冒充新测量、不许把没观测到的东西当空档放过。5 段报告格式(主张 · 证据 · baseline 归因 · 未验证 · 残余风险)绑定每个智能体和编排器的每份完成报告。
削减验证成本,在超额前刹停
验证是必要的;验证输出坐进上下文则不是。冗长的验证输出排到磁盘文件,上下文里只留退出码和截断的尾巴(最多 50 行)。复用提示缓存(缓存读取只要 0.1 倍费用)让窗口保持轻量,上下文瘦身 /clear 策略在阈值(1M 50% / 200K 90%)处发出建议。
预算侧由 token 断路器守着 —— 在硬上限(默认 90%)中止执行,把进度存进 progress.md,并发出一条贴上就能续跑的 resume 消息。状态栏始终显示上下文用量、缓存命中率和限额消耗,超额不会悄悄溜过去。
读懂状态栏
🤖 Opus | 🧠 xhigh·t | ♻️ 87% | 🔅 v2.1.212 | 🗿 v3.1.3 | ⏳ 2h 34m | 💬 MoAI
🪫 CW: ████████░░ 88% (⚠️/clear) | 🔋 5H: ████░░░░░░ 45% (4h 30m) | 🪫 7D: ████████░░ 82% (Jan 21)
📁 moai-adk-go | 📡 modu-ai/moai-adk, 7/3 | 🅱️ [WT] release/v3.1.3 +3 | 💾 +1 M2 ?0 | 📋 [run SPEC-AUTH-001-run] | 💌 PR #1042 (⌥approved)
🏷️ run | 👤 manager-develop | 🔄 TODO: 1/3
| 元素 | 含义 |
|---|---|
| 🤖 模型 | 当前活动模型 |
| 🧠 effort | 推理强度 —— 扩展推理开启时带 ·t 后缀 |
| ♻️ 缓存命中率 | 提示缓存命中率 |
| CW: 上下文 | 上下文窗口使用率 + 两段式 /clear 标记(⚠️ 软性、🛑 硬性) |
| 5H / 7D | 套餐使用率 + 重置时间 |
| 📁 目录 | 项目目录名 |
| 📡 仓库 | GitHub 仓库 owner/name + 打开的 issue/PR 数对(, 7/3;读不到则 , -/-) |
| 🅱️ 分支 | 当前分支 —— 工作树会标 [WT],+ 是改动数(已修改+已暂存+未跟踪) |
| 💾 git 状态 | 已暂存 + · 已修改 M · 未跟踪 ? 计数 —— 任何状态下都只用 💾 一个图标 |
| 📋 任务 | 活动 SPEC 工作流 [命令 SPEC-ID-阶段] |
| 💌 PR | 活动 GitHub PR 编号 + 评审状态(⌥状态) |
| 🏷️ 会话行 | 末行按条件显示 —— 会话名 · 👤 智能体 · 🔄 TODO: 进行中/待办 积压 |
详见:状态栏指南
工作流示例
做一个新功能(TDD)
/moai plan "添加用户头像上传"
/moai run SPEC-PROFILE-001
/moai sync SPEC-PROFILE-001
新代码或覆盖率足够的代码配 TDD(RED → GREEN → REFACTOR)。moai init 检测项目状态,在 TDD 和 DDD 里挑一个。
长时间运行(goal)
/moai plan "重构支付模块"
/moai run SPEC-PAY-001
/moai goal "go test ./... exits 0 && lint clean, or stop after 20 turns"
声明完成条件,会话就自主工作直到条件满足。轮次上限默认 30,并绑着停滞守卫。上下文到阈值(1M 50% / 200K 90%)时建议 /clear,并把进度存进 progress.md。
并行运行(worktree)
moai cc -w feature-auth # 打开 auth 工作树
moai cc -w feature-billing --spawn # billing 开新窗口,保留当前会话
# 在 auth 树里
/moai run SPEC-AUTH-001
# 在 billing 树里
/moai run SPEC-BILL-001
每个 SPEC 独占一棵工作树,两个智能体互不踩踏。分支状态守卫拦住主检出里误切的分支。
降低成本(CG 模式)
moai glm sk-your-glm-api-key # 存一次密钥
moai cg # 进入 CG 模式(Claude 主控 + GLM 工人)
/moai run SPEC-DATA-001 # 实现密集型工作 → GLM 工人扛大批量实现
CG 模式由 Claude 主控负责战略、计划和审计,GLM 工人扛大批量实现 —— 在实现密集型工作上省 60–70%。框架、SPEC 工作流和质量门禁在三种模式下完全一致。
自动抓 bug(loop)
/moai loop
并行扫过 LSP 诊断、AST-grep 和 linter,按级别归组抓到的问题,直到队列清空。单个问题用 /moai fix 一趟了结。
配置与档案
.moai/config/sections/
项目配置拆成一组 YAML 切面文件。moai init 铺下的切面一共 33 个,其中经常动的是下面这六个。
| 切面 | 职责 |
|---|---|
language.yaml |
用户名 · 对话语言 · 代码注释语言 · 提交信息语言 |
quality.yaml |
质量门禁 · 开发模式(TDD/DDD)· 覆盖率 |
harness.yaml |
框架深度(minimal · standard · thorough)· 自动检测 |
workflow.yaml |
工作流行为 |
lsp.yaml |
LSP 门禁阈值(SSOT) |
user.yaml |
用户信息 |
v3.1.1 又多了四个值得一动的切面。
| 切面 | 职责 |
|---|---|
crosssession.yaml |
跨会话消息的处置。inbound(留空 · accept · hold · refuse)、isolate_machines(本机之外的消息是否要求审批)、dialog_expiry(被挂起的消息,其审批对话的期限) |
cache.yaml |
提示缓存的配置文件。存放 session_ttl(1h · 5m · off)、spec_ttl 和值得缓存的最小片段大小,在 moai web 设置编辑器里原样往返。但目前没有代码读取这些值,改了也不会改变行为 |
state.yaml |
home_retention_days —— moai clean --home 从几天前的东西开始清。只从 HOME 层(~/.moai/config/sections/state.yaml)读取,默认 30 天,填 0 就关掉主目录清理 |
statusline.yaml |
在原有的主题与段落开关之上多了 forge 键。取 github · gitlab · none 之一,决定状态栏在哪个托管平台上统计打开中的工作。留空则按 origin 远端的主机判断,所以自建实例上要自己写明 |
gate.yaml 的 ast_grep_gate.rules_dir 不是新键,而是默认值变了的键。原本是空字符串的默认值现在成了 .moai/config/astgrep-rules,moai init/moai update 会把捆绑的规则集铺在那个位置。代码一侧的回退路径已经取消,所以这个键现在是规则集位置的唯一出处 —— 把规则挪到别处的话,这个值也要一起改,门禁才找得到规则。
环境变量覆盖文件值。优先级细节和完整切面清单见 CLI 参考。
模型档案 —— high / medium / low
moai model profile 解析 11 个智能体 × 3 个档案 = 33 个单元的 {model, effort} 组合。
| 档案 | 性格 | 何时用 |
|---|---|---|
| high | 以 Opus 为主、高推理 | 复杂规划 · 安全审计 · 疑难调试 |
| medium(默认) | 均衡 | 常规 SPEC |
| low | Sonnet + 低推理 | 机械重复 · 文档 · 单发任务 |
指派跟着工作阶段(plan / run / sync)和 SPEC 尺寸(Tier S / M / L)走 —— 需要深推理的规划阶段配强推理模型,机械重复的实现阶段配轻量模型。按 No-Haiku 三档策略,单发、输入主导的任务交给 Sonnet low,所有多轮智能体任务一律交给 Opus。
settings.json / settings.local.json 分离
| 文件 | 职责 | 模板 |
|---|---|---|
.claude/settings.json |
从模板渲染 —— 项目共享配置 | 包含 |
.claude/settings.local.json |
运行时管理 —— 每台机器的值(tmux pane ID · API 令牌 · 绝对路径) | 绝不包含 |
settings.local.json 由 moai glm、moai cc、moai cg 在运行时修改,SessionStart 钩子填充环境。误提交了就用 git rm --cached .claude/settings.local.json 摘掉。
随处可用
16 种编程语言同等支持
| Go | Python | TypeScript | JavaScript |
| Rust | Java | Kotlin | C# |
| Ruby | PHP | Elixir | C++ |
| Scala | R | Flutter | Swift |
按项目标记自动检测每种语言,并运行该语言的标准 lint/格式化/测试工具链。缺失的工具悄悄跳过。Dart/Flutter 的正式名称是 "flutter"。没有任何一种受到优待。
四语言区文档
| 语言区 | 站点 |
|---|---|
| 한국어 | adk.mo.ai.kr/ko |
| English | adk.mo.ai.kr/en |
| 日本語 | adk.mo.ai.kr/ja |
| 中文 | adk.mo.ai.kr/zh |
四个语言区在同一 PR 内维护,四区一致性检查绑在构建门禁上。禁止翻译腔,每种语言各有母语行文。
操作系统
| 平台 | 状态 |
|---|---|
| macOS | 完全支持(Terminal、iTerm2) |
| Linux | 完全支持(Bash、Zsh) |
| Windows | 推荐 WSL,支持 PowerShell 7.x+,原生 cmd.exe 不支持 |
Claude + GLM
z.ai GLM 作为 Claude Code 的替代后端。只换环境变量,代码原样不动。共三种运行模式。
| 命令 | 主控 | 工人 | tmux | 省成本 |
|---|---|---|---|---|
moai cc |
Claude | Claude | 不需要 | — |
moai glm |
GLM | GLM | 建议 | 约 70% |
moai cg |
Claude | GLM | 必需 | 约 60% |
GLM Coding Plan 每月 $10 起。可用 glm-5.3-flash(默认)、glm-5.3、glm-4.7、glm-4.5-air 以及免费模型(GLM-4.7-Flash、GLM-4.5-Flash)。
Claude 的每一档通过 ANTHROPIC_DEFAULT_*_MODEL 环境变量映射到 GLM 模型:
| Claude 档位 | GLM 模型 | 上下文 |
|---|---|---|
| Opus | glm-5.3-flash | 1M |
| Sonnet | glm-5.3-flash | 1M |
| Haiku | glm-5.3-flash | 1M |
| Fable | glm-5.3-flash | 1M |
glm-5.3 在任何档位插槽都仍然可选(
llm.yaml的llm.glm.models.*);把插槽改回去也只是一行配置改动。
详见:Multi-LLM 指南 · z.ai 定价
文档与学习
官方文档 —— adk.mo.ai.kr
adk.mo.ai.kr 在线文档分为 12 个板块。
| 板块 | 说明 |
|---|---|
| 快速上手 | 简介 · 安装 · Windows 指南 · init 向导 · 快速入门 · CLI 概览 · FAQ |
| 核心概念 | 身份 · 宪章 · 框架工程 · 基于 SPEC 的开发 · DDD · TRUST 5 |
| 工作流命令 | plan · run · sync —— SPEC 流水线主轴 |
| 实用命令 | fix · loop · gate · review · clean · codemaps · e2e · feedback · goal · todo |
| CLI 参考 | 终端 moai 二进制的全部命令(共 49 个) |
| Claude Code 指南 | Claude Code 集成 —— 基础 · 上下文/记忆 · 智能体 · 扩展性 |
| Multi-LLM | CG 模式与模型策略 |
| 成本优化 | 提示缓存策略与 token 成本削减 |
| 指南 | CI 自治化 · 多 LLM CI 等实战运维配方 |
| Git Worktree | 并行 SPEC 开发的工作树指南 |
| Advanced | token 经济学 · token 预算 · 状态栏 · settings.json · 钩子 · @MX 标签 · 技能 · Harness v4 Builder · 自我进化 · 决策记忆 |
| 参与贡献 | 开源贡献指南 |
图书
用 Claude Code 开始实战智能体编程 —— moai-adk 作者写的实战框架工程指南。book.mo.ai.kr
CLI 命令表(常用 17 个)
| 命令 | 说明 |
|---|---|
moai init |
交互式项目初始化(自动检测语言/框架/方法论) |
moai doctor |
系统状态诊断与环境校验 —— Home Disk Usage 项会告诉你 ~/.moai 膨胀到了多大 |
moai status |
项目状态摘要(Git 分支、质量指标) |
moai update |
升级到最新版(删除前备份 · 支持自动回滚) |
moai graph <build|query> |
生成/查询代码库图(edges.jsonl)—— 找调用方、波及范围、里程碑交叉检查 |
moai cc / moai glm / moai cg |
Claude 专用 / GLM 专用 / 混合会话 |
moai worktree <sync|done|remove|clean|recover|snapshot|verify|restore> |
Git worktree 维护(进出工作树是启动器的职责) |
moai session <list|register|current> |
多会话协调 |
moai spec <audit|archive|lint|list|new> |
SPEC 生命周期工具 |
moai goal <arm|status|clear> |
goal 引擎 CLI |
moai harness <status|apply|rollback|disable> |
框架学习生命周期 |
moai handoff <save|list> |
会话交接记录 |
moai preference <list|decay-scan|toggle> |
决策记忆管理 |
moai memory <doctor|archive> |
智能体记忆体检与旧条目归档 |
moai tokens record |
按池记录 token 使用台账 |
moai clean [--home] |
清理旧的运行产物。加上 --home 就在允许清单范围内清理 ~/.moai。默认是 dry-run,要加 --force 才真正删除 |
moai web |
网页控制台 —— 6 个画面(Overview · Kanban · Specs · Monitor · Settings · Todo)、11 标签页设置 |
全部 49 个命令:CLI 参考
ref / domain 技能
ref(现场知识)11 个:moai-ref-api-patterns、moai-ref-owasp-checklist、moai-ref-llm-security、moai-ref-react-patterns、moai-ref-testing-pyramid、moai-ref-ui-polish、moai-ref-secops、moai-ref-supply-chain、moai-ref-seo、moai-ref-git-workflow、moai-ref-cross-model-audit
domain(专业领域)7 个:moai-domain-backend、moai-domain-frontend、moai-domain-database、moai-domain-design-dna、moai-domain-html-report、moai-domain-humanize、moai-domain-svg-infographic
moai-domain-design-dna 是 v3.1.1 新加的。给它一份要参考的设计 —— 截图也好、一组图片也好、活的 URL 也好 —— 它会把颜色、间距、圆角、字体这些量得出来的值,连同那份设计的气质和特殊渲染效果,一并反推成一份 Design DNA JSON。再把这份 JSON 喂回去,它就造出气质相同的新产物 —— 这是把“做成这个画面的样子”从话语搬到数值上的路径。也支持图表配置档案:激活档案的标记保存在项目根目录的 .design-dna/ 下,能活过 moai update;可选开启的 mermaid、drawio 导入器把来源当作不可信输入 —— 坐标、颜色、字体、版式一概不带过来。
CHANGELOG
最近的变更见 CHANGELOG.md。
代码质量要求
每次贡献都要过 TRUST 5 门禁 —— 覆盖率 85% 以上 · lint 错误 0 · 类型错误 0 · Conventional commits。存量代码先用特征化测试固定行为再渐进改进(DDD),新代码走 RED → GREEN → REFACTOR(TDD)。
常见问题
为什么不是每个函数都有 @MX 标签?
正常。标签只标高扇入、复杂或危险的代码。任何项目里的大多数代码都够不到标签阈值 —— 没有标签的文件不是缺陷。
状态栏里的版本显示是什么意思?
🗿 v3.1.2 -> 🗿 v3.1.3
前一个值是当前安装的 moai-adk 版本,箭头表示有可用更新。运行 moai update 后消失。
不用 GLM、只用 Claude 可以吗?
可以。moai cc 启动纯 Claude 会话。CG 模式(moai cg,Claude 主控 + GLM 工人)和纯 GLM(moai glm)是省钱选项;框架、SPEC 工作流和质量门禁在三种模式下完全一致。
在已有项目上能用吗?
能。moai init 检测项目状态并选择方法论 —— 覆盖率低于 10% 的存量代码用 DDD(先特征化测试固定行为、再渐进改进),新项目或测试充分的代码用 TDD。
一起参与
贡献
随时欢迎贡献。详细流程见 CONTRIBUTING.md。
- 复刻(fork)仓库
- 建功能分支:
git checkout -b feature/my-feature - 写测试 —— 新代码用 TDD,存量代码用特征化测试
- 确认测试、lint、格式化通过:
make test·make lint·make fmt - 用 Conventional commit 信息提交并开拉取请求
代码质量要求:覆盖率 85% 以上 · lint 错误 0 · 类型错误 0 · Conventional commits
反馈
在 Claude Code 里用 /moai feedback 直接把 bug 报告和功能请求发成 GitHub issue。终端里则用 GitHub Issues。
社区
- Discord —— 实时讨论与技巧
- GitHub Issues —— bug 报告 · 功能请求
许可证
Apache License 2.0 —— 详情见 LICENSE 文件。
Star 历史
MoAI-ADK 团队出品 · adk.mo.ai.kr
项目介绍
面向 Claude Code 的规范优先智能开发套件 —— 24 个 AI 智能体 + 52 项技能,配备 TDD/DDD 质量关卡,支持 16 种语言项目、4 种语言文档。Go 命令行界面,零依赖。【此简介由AI生成】