Skip to content

Add Canon loop scaffolding and reporter/cli sample modules - #16

Merged
andreidita22 merged 6 commits into
mainfrom
canon-loop-scaffold
Oct 19, 2025
Merged

Add Canon loop scaffolding and reporter/cli sample modules#16
andreidita22 merged 6 commits into
mainfrom
canon-loop-scaffold

Conversation

@andreidita22

Copy link
Copy Markdown
Contributor

Summary

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @andreidita22, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request lays the groundwork for a robust and verifiable module development process by introducing the "Canon loop." It defines a strict contract for modules through a new lock file, provides concrete examples with "reporter" and "cli" modules, and integrates a verification script to enforce these standards. The changes aim to streamline development, improve consistency, and ensure that all modules meet predefined architectural and functional requirements before deployment.

Highlights

  • Canon Loop Scaffolding: Introduced "amr/canon.lock.json" to define the expected structure for modules, their ports, invariants, and acceptance tests, establishing a core contract for module development.
  • Sample Modules: Added fully functional "reporter" and "cli" modules, complete with "module.json" manifests, source code (".js", ".d.ts"), and comprehensive test suites (acceptance and invariant tests), serving as concrete examples for the Canon loop.
  • Workflow Documentation: Updated various documentation files ("docs/briefs.md", "docs/headless-bo4-runbook.md", "docs/headless-cloud.md", "docs/implementation-briefs.md") to detail the Canon loop workflow, including self-check procedures, reporting requirements, and remediation steps for common failures.
  • Canon Verification Script: Implemented "scripts/canon-verify.mjs", a new utility that programmatically verifies module implementations against the "canon.lock.json", ensuring adherence to defined contracts and catching discrepancies early.
  • Standardized Reporting: Introduced "implementation_report.json.template" to standardize the reporting of implementation results, providing a clear structure for documenting module status after a successful Canon loop run.
  • Port Definitions: Extended the TypeScript runtime port definitions ("runtimes/ts/ports/index.d.ts", "runtimes/ts/ports/index.ts") to include the new "ReporterPort" and "CLIPort" interfaces, making them available for type-safe integration.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the "Canon loop" concept, adding significant scaffolding, documentation, and two new sample modules: reporter and cli. The changes are extensive and well-documented, establishing a clear process for module implementation and verification. The new canon-verify.mjs script is a great addition for ensuring conformance. My review focuses on improving the robustness and efficiency of the new module implementations and their test runners. I've identified a few areas for improvement in argument parsing, file I/O efficiency, and test cleanup logic to prevent race conditions.

Comment thread modules/cli/src/cli.js Outdated
Comment on lines +7 to +12
if (tokens.length && tokens[0].includes('node')) {
tokens.shift();
}
if (tokens.length && tokens[0].endsWith('.js')) {
tokens.shift();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The logic for stripping the node executable and script name from argv is fragile. It relies on includes('node') and endsWith('.js'), which can lead to incorrect behavior with arguments that happen to match these patterns (e.g., a file argument named node.js). This could cause arguments to be silently dropped.

A more robust approach would be to expect the caller to provide a clean argument array (e.g., by passing process.argv.slice(2)), which is a common and robust pattern for reusable CLI modules. This makes the module's behavior more predictable.

Suggested change
if (tokens.length && tokens[0].includes('node')) {
tokens.shift();
}
if (tokens.length && tokens[0].endsWith('.js')) {
tokens.shift();
}
// The cliParse function expects that `argv` has been pre-processed
// to remove the node executable and script name.
const tokens = argv.slice();

Comment thread modules/cli/src/cli.js Outdated
Comment on lines +32 to +61
for (let i = 0; i < tokens.length; i += 1) {
const token = tokens[i];
if (token.startsWith('--')) {
const [rawKey, rawValue] = token.slice(2).split('=', 2);
if (!rawKey) {
result.errors.push('Invalid flag syntax.');
continue;
}
let value = rawValue;
if (value === undefined) {
const next = tokens[i + 1];
if (next && !next.startsWith('-')) {
value = next;
i += 1;
} else {
value = true;
}
}
result.options[rawKey] = value;
continue;
}
if (token.startsWith('-')) {
const flags = token.slice(1).split('');
for (const flag of flags) {
result.options[flag] = true;
}
continue;
}
result.positionals.push(token);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This for loop, which manually increments the index i both in the loop definition and within the loop body, can be hard to follow and is prone to off-by-one errors. Refactoring this to a while loop that consumes tokens from the array using tokens.shift() would make the logic clearer and less error-prone, as you would not need to manage the index manually.

Here's a conceptual example:

const tokens = ...; // remaining tokens
while (tokens.length > 0) {
  const token = tokens.shift();
  if (token.startsWith('--')) {
    // ... logic for long options
    if (value === undefined) {
      const next = tokens[0]; // peek
      if (next && !next.startsWith('-')) {
        value = tokens.shift(); // consume value
      }
    }
  } else {
    // ...
  }
}

Comment thread modules/reporter/src/index.js Outdated
Comment on lines +40 to +43
if (!alreadyLast) {
prior.push(trimmed);
await fs.writeFile(logFile, `${prior.join('\n')}\n`, 'utf8');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation reads the entire log file into memory, modifies the array, and then writes the entire contents back to disk on every call. This can become inefficient if the log file grows very large, leading to high memory usage and slow I/O.

Using fs.appendFile is more efficient for this use case as it avoids rewriting the entire file. The in-memory prior array can still be updated after a successful append to ensure the correct lines are returned.

Suggested change
if (!alreadyLast) {
prior.push(trimmed);
await fs.writeFile(logFile, `${prior.join('\n')}\n`, 'utf8');
}
if (!alreadyLast) {
await fs.appendFile(logFile, `${trimmed}\n`, 'utf8');
prior.push(trimmed);
}

Comment thread modules/reporter/tests/runner.mjs Outdated
}
} finally {
await fs.rm(logDir, { recursive: true, force: true });
await fs.rm(path.join(moduleRoot, '.tmp'), { recursive: true, force: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This line, which removes the parent .tmp directory, can introduce a race condition if tests are run in parallel. Another test might be using a different scratch directory under .tmp, and this would delete it prematurely, causing the other test to fail. The cleanup should be limited to the scratch directory created for this specific test case (logDir). The parent .tmp directory should be cleaned up in a global test suite teardown, not within each test case.

Comment thread scripts/canon-verify.mjs Outdated
}

const lockPath = args.lock;
const modulesRoot = args['modules-root'] || args.modulesRoot;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The expression args['modules-root'] || args.modulesRoot is redundant. The parseArgs function creates object keys from arguments like --modules-root as modules-root. Therefore, args.modulesRoot will always be undefined. While this doesn't cause a bug, it's confusing and can be simplified.

Suggested change
const modulesRoot = args['modules-root'] || args.modulesRoot;
const modulesRoot = args['modules-root'];

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread modules/cli/src/cli.js
Comment on lines +6 to +11
const tokens = argv.slice();
if (tokens.length && tokens[0].includes('node')) {
tokens.shift();
}
if (tokens.length && tokens[0].endsWith('.js')) {
tokens.shift();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Strip .mjs/.cjs entry script before command parsing

The parser trims a node binary and then only removes the next token when it ends with .js. When the CLI is executed as an ES module (e.g. node cli.mjs report …), the .mjs script path remains in tokens, so the parser treats the path itself as the command and flags every invocation as Unknown command. This repo already ships scripts with .mjs extensions, so the CLI will misbehave in normal usage until the code recognises module file extensions (or more generally strips any second argument that looks like a script file).

Useful? React with 👍 / 👎.

@andreidita22
andreidita22 merged commit 660c24b into main Oct 19, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant